mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Card gci folder update (#135)
* add ability to set path of card image before CARDInit * begin work on updating card class to support gci folders a lot of work will just be directly reading from whatever folder is supplied, instead of relying on an always open FileIO handle * swap to interface system for easier code seperation, begin actually planning out logic for gci loading * fix wrong name for func, add modified time to new gci file entry * get gci folder working tp seems to be happy with everything ive done, the only funcs im worried about are getSerial and getChecksum as those dont do anything atm. * fix createFile not assigning file index to output FileHandle, CARDSetLoadType now takes an enum * make probeCardFile a virtual func so GciFolder and RawFile can both implement their own, bit of cleanup
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
add_library(aurora_card STATIC
|
||||
lib/card/BlockAllocationTable.cpp
|
||||
lib/card/Card.cpp
|
||||
lib/card/CardRawFile.cpp
|
||||
lib/card/CardGciFolder.cpp
|
||||
lib/card/Directory.cpp
|
||||
lib/card/DolphinCardPath.cpp
|
||||
lib/card/File.cpp
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef _DOLPHIN_CARD_H_
|
||||
#define _DOLPHIN_CARD_H_
|
||||
|
||||
#include <string_view>
|
||||
#include <dolphin/os.h>
|
||||
#include <dolphin/dsp.h>
|
||||
#include <dolphin/dvd.h>
|
||||
@@ -13,6 +14,15 @@ extern "C" {
|
||||
#define CARD_MAX_FILE 127
|
||||
#define CARD_ICON_MAX 8
|
||||
|
||||
#if TARGET_PC
|
||||
|
||||
enum CARDFileType {
|
||||
RawImage,
|
||||
GciFolder,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
typedef void (*CARDCallback)(s32 chan, s32 result);
|
||||
|
||||
typedef struct CARDFileInfo {
|
||||
@@ -240,6 +250,7 @@ void CARDSetGameAndMaker(const s32 chan, const char* game, const char* maker);
|
||||
void CARDDetectDolphin(s32 chan);
|
||||
// pass -1 to set both
|
||||
void CARDSetBasePath(const char*, s32 chan);
|
||||
void CARDSetLoadType(CARDFileType type);
|
||||
|
||||
#else
|
||||
void CARDInit(void);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace aurora::card {
|
||||
class BlockAllocationTable {
|
||||
friend class Card;
|
||||
friend class CardRawFile;
|
||||
#pragma pack(push, 4)
|
||||
union {
|
||||
struct {
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
#include "CardGciFolder.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "Directory.hpp"
|
||||
#include "FileIO.hpp"
|
||||
#include "../internal.hpp"
|
||||
|
||||
namespace {
|
||||
aurora::Module Log("aurora::card");
|
||||
}
|
||||
|
||||
namespace aurora::card {
|
||||
|
||||
CardGciFolder::GciFile* CardGciFolder::getFile(uint32_t idx) {
|
||||
if (m_files.size() > idx) {
|
||||
auto file = &m_files[idx];
|
||||
if (file->opened)
|
||||
return file;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CardGciFolder::GciFile* CardGciFolder::getFile(FileHandle& fh) { return getFile(fh.getFileNo()); }
|
||||
|
||||
const CardGciFolder::GciFile* CardGciFolder::getFile(uint32_t idx) const {
|
||||
if (m_files.size() > idx) {
|
||||
auto file = &m_files[idx];
|
||||
if (file->opened)
|
||||
return file;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CardGciFolder::GciFile* CardGciFolder::getFile(FileHandle& fh) const { return getFile(fh.getFileNo()); }
|
||||
|
||||
CardGciFolder::CardGciFolder() {}
|
||||
|
||||
CardGciFolder::CardGciFolder(CardGciFolder&& other) {
|
||||
m_files = std::move(other.m_files);
|
||||
m_bat = std::move(other.m_bat);
|
||||
m_folderPath = other.m_folderPath;
|
||||
m_encoding = other.m_encoding;
|
||||
|
||||
CardGciFolder::setCurrentGame(other.m_game);
|
||||
CardGciFolder::setCurrentMaker(other.m_maker);
|
||||
}
|
||||
|
||||
CardGciFolder& CardGciFolder::operator=(CardGciFolder&& other) { return *this; }
|
||||
|
||||
void CardGciFolder::InitCard(const char* game, const char* maker) {
|
||||
setCurrentGame(game);
|
||||
setCurrentMaker(maker);
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::openFile(const char* filename, FileHandle& handleOut) {
|
||||
int idx = 0;
|
||||
for (auto& gciFile : m_files) {
|
||||
if (strcmp(filename, gciFile.file.m_filename) == 0) {
|
||||
gciFile.opened = true;
|
||||
if (gciFile.fileSize == 0)
|
||||
gciFile.fileSize = std::filesystem::file_size(m_folderPath / gciFile.filename);
|
||||
|
||||
handleOut = FileHandle(idx, 0);
|
||||
return ECardResult::READY;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return ECardResult::NOCARD;
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::openFile(uint32_t fileno, FileHandle& handleOut) {
|
||||
if (m_files.size() > fileno) {
|
||||
auto& gciFile = m_files[fileno];
|
||||
handleOut = FileHandle(fileno, 0);
|
||||
gciFile.opened = true;
|
||||
if (gciFile.fileSize == 0)
|
||||
gciFile.fileSize = std::filesystem::file_size(m_folderPath / gciFile.filename);
|
||||
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
return ECardResult::NOCARD;
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::createFile(const char* filename, size_t size, FileHandle& handleOut) {
|
||||
std::string gciFilename = fmt::format("{}-{}-{}.gci", m_maker, m_game, filename);
|
||||
uint16_t neededBlocks = ROUND_UP_8192(size) / BlockSize;
|
||||
size_t fileSize = sizeof(File) + size;
|
||||
constexpr uint16_t maxBlocks = ((uint16_t)ECardSize::Card2043Mb * MbitToBlocks) - FSTBlocks;
|
||||
|
||||
std::vector<uint8_t> fileBuf(fileSize);
|
||||
|
||||
File* gciFileHeader = (File*)fileBuf.data();
|
||||
|
||||
std::memcpy(gciFileHeader->m_game, m_game, 4);
|
||||
std::memcpy(gciFileHeader->m_maker, m_maker, 2);
|
||||
strncpy(gciFileHeader->m_filename, filename, std::size(gciFileHeader->m_filename));
|
||||
|
||||
gciFileHeader->m_modifiedTime = static_cast<uint32_t>(getGCTime());
|
||||
gciFileHeader->m_blockCount = neededBlocks;
|
||||
gciFileHeader->m_firstBlock = m_bat.allocateBlocks(neededBlocks, maxBlocks);
|
||||
|
||||
m_files.push_back({*gciFileHeader, fileSize, gciFilename, false}); // push non-endian swapped header first
|
||||
|
||||
handleOut = FileHandle(m_files.size() - 1, 0);
|
||||
|
||||
// write big endian header for dolphin compat
|
||||
gciFileHeader->swapEndian();
|
||||
FileIO file((m_folderPath / gciFilename).string(), true);
|
||||
file.fileWrite(fileBuf.data(), fileBuf.size(), 0);
|
||||
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::closeFile(FileHandle& fh) {
|
||||
auto file = getFile(fh);
|
||||
if (file) {
|
||||
file->opened = false;
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
return ECardResult::NOFILE;
|
||||
}
|
||||
|
||||
void CardGciFolder::deleteFile(const FileHandle& fh) {
|
||||
auto file = getFile(fh.getFileNo());
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
FileIO fileIO((m_folderPath / file->filename).string(), true);
|
||||
if (fileIO)
|
||||
fileIO.deleteFile();
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::deleteFile(const char* filename) { return ECardResult::NOCARD; }
|
||||
|
||||
ECardResult CardGciFolder::deleteFile(uint32_t fileno) { return ECardResult::NOCARD; }
|
||||
|
||||
ECardResult CardGciFolder::renameFile(const char* oldName, const char* newName) {
|
||||
for (auto& gciFile : m_files) {
|
||||
if (strcmp(oldName, gciFile.file.m_filename) == 0) {
|
||||
strncpy(gciFile.file.m_filename, newName, std::size(gciFile.file.m_filename));
|
||||
return ECardResult::READY;
|
||||
}
|
||||
}
|
||||
|
||||
return ECardResult::NOCARD;
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::fileWrite(FileHandle& fh, const void* buf, size_t size) {
|
||||
auto file = getFile(fh);
|
||||
if (file) {
|
||||
FileIO fileIO((m_folderPath / file->filename).string());
|
||||
if (fileIO) {
|
||||
if (fileIO.fileWrite(buf, size, sizeof(File) + fh.offset))
|
||||
return ECardResult::READY;
|
||||
return ECardResult::IOERROR;
|
||||
}
|
||||
return ECardResult::NOFILE;
|
||||
}
|
||||
|
||||
return ECardResult::NOCARD;
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::fileRead(FileHandle& fh, void* dst, size_t size) {
|
||||
auto file = getFile(fh);
|
||||
if (file) {
|
||||
FileIO fileIO((m_folderPath / file->filename).string());
|
||||
if (fileIO) {
|
||||
if (fileIO.fileRead(dst, size, sizeof(File) + fh.offset))
|
||||
return ECardResult::READY;
|
||||
return ECardResult::IOERROR;
|
||||
}
|
||||
return ECardResult::NOFILE;
|
||||
}
|
||||
|
||||
return ECardResult::NOCARD;
|
||||
}
|
||||
|
||||
void CardGciFolder::seek(FileHandle& fh, int32_t pos, SeekOrigin whence) {
|
||||
auto file = getFile(fh);
|
||||
if (file) {
|
||||
switch (whence) {
|
||||
case SeekOrigin::Begin:
|
||||
fh.offset = pos;
|
||||
break;
|
||||
case SeekOrigin::Current:
|
||||
fh.offset += pos;
|
||||
break;
|
||||
case SeekOrigin::End:
|
||||
fh.offset = file->fileSize - pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::getStatus(const FileHandle& fh, CardStat& statOut) const {
|
||||
return getStatus(fh.getFileNo(), statOut);
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
auto gciFile = getFile(fileNo);
|
||||
|
||||
if (!gciFile)
|
||||
return ECardResult::NOFILE;
|
||||
const auto file = &gciFile->file;
|
||||
|
||||
std::strncpy(statOut.x0_fileName, file->m_filename, 32);
|
||||
statOut.x20_length = file->m_blockCount * BlockSize;
|
||||
statOut.x24_time = file->m_modifiedTime;
|
||||
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;
|
||||
statOut.x34_iconFormat = file->m_iconFmt;
|
||||
statOut.x36_iconSpeed = file->m_animSpeed;
|
||||
statOut.x38_commentAddr = file->m_commentAddr;
|
||||
|
||||
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;
|
||||
statOut.x3c_offsetBanner = cur;
|
||||
cur += BannerSize(statOut.GetBannerFormat());
|
||||
statOut.x40_offsetBannerTlut = cur;
|
||||
cur += TlutSize(statOut.GetBannerFormat());
|
||||
bool palette = false;
|
||||
for (size_t i = 0; i < statOut.x44_offsetIcon.size(); ++i) {
|
||||
statOut.x44_offsetIcon[i] = cur;
|
||||
const EImageFormat fmt = statOut.GetIconFormat(static_cast<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 = UINT32_MAX;
|
||||
statOut.x68_offsetData = cur;
|
||||
}
|
||||
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::setStatus(const FileHandle& fh, const CardStat& stat) {
|
||||
return setStatus(fh.getFileNo(), stat);
|
||||
}
|
||||
|
||||
ECardResult CardGciFolder::setStatus(uint32_t fileNo, const CardStat& stat) {
|
||||
auto gciFile = getFile(fileNo);
|
||||
|
||||
if (!gciFile)
|
||||
return ECardResult::NOFILE;
|
||||
const auto file = &gciFile->file;
|
||||
|
||||
file->m_bannerFlags = stat.x2e_bannerFormat;
|
||||
file->m_iconAddress = stat.x30_iconAddr;
|
||||
file->m_iconFmt = stat.x34_iconFormat;
|
||||
file->m_animSpeed = stat.x36_iconSpeed;
|
||||
file->m_commentAddr = stat.x38_commentAddr;
|
||||
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
void CardGciFolder::setCurrentGame(const char* game) {
|
||||
if (game != nullptr && std::strlen(game) == 4) {
|
||||
std::memcpy(m_game, game, 4);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* CardGciFolder::getCurrentGame() const {
|
||||
if (std::strlen(m_game) == sizeof(m_game) - 1) {
|
||||
return reinterpret_cast<const uint8_t*>(m_game);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CardGciFolder::setCurrentMaker(const char* maker) {
|
||||
if (maker != nullptr && std::strlen(maker) == 2) {
|
||||
std::memcpy(m_maker, maker, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* CardGciFolder::getCurrentMaker() const {
|
||||
if (std::strlen(m_maker) == sizeof(m_maker) - 1) {
|
||||
return reinterpret_cast<const uint8_t*>(m_maker);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CardGciFolder::getSerial(uint64_t& serial) {
|
||||
serial = 0; // TODO
|
||||
}
|
||||
|
||||
void CardGciFolder::getChecksum(uint16_t& checksum, uint16_t& inverse) const {
|
||||
// TODO
|
||||
checksum = 0;
|
||||
inverse = 0;
|
||||
}
|
||||
|
||||
void CardGciFolder::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const {
|
||||
bytesNotUsed = m_bat.numFreeBlocks() * BlockSize;
|
||||
filesNotUsed = MaxFiles;
|
||||
}
|
||||
|
||||
void CardGciFolder::getEncoding(uint16_t& encoding) const { encoding = (uint16_t)m_encoding; }
|
||||
|
||||
void CardGciFolder::format(ECardSlot deviceId, ECardSize size, EEncoding encoding) {
|
||||
m_encoding = encoding;
|
||||
|
||||
if (!std::filesystem::create_directories(m_folderPath)) {
|
||||
Log.error("Failed to create directory: {}", m_folderPath.string());
|
||||
}
|
||||
}
|
||||
|
||||
void CardGciFolder::commit() {
|
||||
for (auto& gciFile : m_files) {
|
||||
if (gciFile.opened) {
|
||||
FileIO file((m_folderPath / gciFile.filename).string());
|
||||
|
||||
File tempFile = gciFile.file;
|
||||
tempFile.swapEndian();
|
||||
file.fileWrite(&tempFile, sizeof(File), 0); // update header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CardGciFolder::open(std::string_view filepath) {
|
||||
m_folderPath = filepath;
|
||||
|
||||
if (!std::filesystem::exists(filepath) || !std::filesystem::is_directory(filepath))
|
||||
return false;
|
||||
|
||||
for (const auto& dir : std::filesystem::directory_iterator(filepath)) {
|
||||
if (!dir.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
auto path = dir.path();
|
||||
|
||||
if (path.extension() != ".gci")
|
||||
continue;
|
||||
|
||||
FileIO file(path.string());
|
||||
|
||||
File fileData;
|
||||
file.fileRead((void*)&fileData, sizeof(File), 0);
|
||||
fileData.swapEndian();
|
||||
|
||||
m_files.push_back({fileData, file.fileSize(), path.filename().string(), false});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CardGciFolder::close() {
|
||||
m_files.clear();
|
||||
m_folderPath = "";
|
||||
m_bat = BlockAllocationTable();
|
||||
}
|
||||
|
||||
std::string_view CardGciFolder::cardFilename() const { return ""; }
|
||||
|
||||
ECardResult CardGciFolder::getError() const { return ECardResult::READY; }
|
||||
|
||||
ProbeResults CardGciFolder::probeCardFile(std::string_view filename) {
|
||||
if (!std::filesystem::exists(filename) || !std::filesystem::is_directory(filename))
|
||||
return {ECardResult::NOCARD, 0, 0};
|
||||
|
||||
for (const auto& dir : std::filesystem::directory_iterator(filename)) {
|
||||
if (!dir.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
auto path = dir.path();
|
||||
|
||||
if (path.extension() != ".gci")
|
||||
continue;
|
||||
|
||||
FileIO file(path.string());
|
||||
|
||||
File fileData;
|
||||
file.fileRead(&fileData, sizeof(File), 0);
|
||||
fileData.swapEndian();
|
||||
|
||||
bool isGameSame = memcmp(fileData.m_game, m_game, std::size(fileData.m_game)) == 0;
|
||||
bool isMakerSame = memcmp(fileData.m_maker, m_maker, std::size(fileData.m_maker)) == 0;
|
||||
|
||||
if (isGameSame && isMakerSame)
|
||||
return {ECardResult::READY, static_cast<uint32_t>(file.fileSize()), BlockSize};
|
||||
}
|
||||
|
||||
return {ECardResult::NOFILE, 0, 0};
|
||||
}
|
||||
|
||||
} // namespace aurora::card
|
||||
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "BlockAllocationTable.hpp"
|
||||
#include "CommonData.h"
|
||||
#include "File.hpp"
|
||||
#include "ICard.hpp"
|
||||
|
||||
namespace aurora::card {
|
||||
|
||||
class CardGciFolder : public ICard {
|
||||
private:
|
||||
struct GciFile {
|
||||
File file;
|
||||
size_t fileSize;
|
||||
std::string filename;
|
||||
bool opened = false;
|
||||
};
|
||||
|
||||
std::vector<GciFile> m_files;
|
||||
std::filesystem::path m_folderPath;
|
||||
BlockAllocationTable m_bat;
|
||||
|
||||
EEncoding m_encoding = EEncoding::ASCII;
|
||||
|
||||
char m_game[5] = {'\0'};
|
||||
char m_maker[3] = {'\0'};
|
||||
|
||||
GciFile* getFile(FileHandle& fh);
|
||||
const GciFile* getFile(FileHandle& fh) const;
|
||||
GciFile* getFile(uint32_t idx);
|
||||
const GciFile* getFile(uint32_t idx) const;
|
||||
public:
|
||||
CardGciFolder();
|
||||
~CardGciFolder() override = default;
|
||||
|
||||
CardGciFolder(const CardGciFolder& other) = delete;
|
||||
CardGciFolder& operator=(const CardGciFolder& other) = delete;
|
||||
CardGciFolder(CardGciFolder&& other);
|
||||
CardGciFolder& operator=(CardGciFolder&& other);
|
||||
|
||||
void InitCard(const char* game, const char* maker) override;
|
||||
|
||||
ECardResult openFile(const char* filename, FileHandle& handleOut) override;
|
||||
ECardResult openFile(uint32_t fileno, FileHandle& handleOut) override;
|
||||
ECardResult createFile(const char* filename, size_t size, FileHandle& handleOut) override;
|
||||
ECardResult closeFile(FileHandle& fh) override;
|
||||
void deleteFile(const FileHandle& fh) override;
|
||||
ECardResult deleteFile(const char* filename) override;
|
||||
ECardResult deleteFile(uint32_t fileno) override;
|
||||
ECardResult renameFile(const char* oldName, const char* newName) override;
|
||||
ECardResult fileWrite(FileHandle& fh, const void* buf, size_t size) override;
|
||||
ECardResult fileRead(FileHandle& fh, void* dst, size_t size) override;
|
||||
void seek(FileHandle& fh, int32_t pos, SeekOrigin whence) override;
|
||||
ECardResult getStatus(const FileHandle& fh, CardStat& statOut) const override;
|
||||
ECardResult getStatus(uint32_t fileNo, CardStat& statOut) const override;
|
||||
ECardResult setStatus(const FileHandle& fh, const CardStat& stat) override;
|
||||
ECardResult setStatus(uint32_t fileNo, const CardStat& stat) override;
|
||||
void setCurrentGame(const char* game) override;
|
||||
const uint8_t* getCurrentGame() const override;
|
||||
void setCurrentMaker(const char* maker) override;
|
||||
const uint8_t* getCurrentMaker() const override;
|
||||
void getSerial(uint64_t& serial) override;
|
||||
void getChecksum(uint16_t& checksum, uint16_t& inverse) const override;
|
||||
void getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const override;
|
||||
void getEncoding(uint16_t& encoding) const override;
|
||||
void format(ECardSlot deviceId, ECardSize size = ECardSize::Card2043Mb, EEncoding encoding = EEncoding::ASCII) override;
|
||||
void commit() override;
|
||||
bool open(std::string_view filepath) override;
|
||||
void close() override;
|
||||
std::string_view cardFilename() const override;
|
||||
ECardResult getError() const override;
|
||||
ProbeResults probeCardFile(std::string_view filename) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Card.hpp"
|
||||
#include "CardRawFile.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
@@ -11,11 +11,9 @@
|
||||
|
||||
namespace aurora::card {
|
||||
|
||||
#define ROUND_UP_8192(val) (((val) + 8191) & ~8191)
|
||||
|
||||
static void NullFileAccess() { fprintf(stderr, "Attempted to access null file\n"); }
|
||||
|
||||
void Card::CardHeader::_swapEndian() {
|
||||
void CardRawFile::CardHeader::_swapEndian() {
|
||||
m_formatTime = bswap(m_formatTime);
|
||||
m_sramBias = bswap(m_sramBias);
|
||||
m_sramLanguage = bswap(m_sramLanguage);
|
||||
@@ -28,9 +26,9 @@ void Card::CardHeader::_swapEndian() {
|
||||
m_checksumInv = bswap(m_checksumInv);
|
||||
}
|
||||
|
||||
Card::Card() { m_ch.raw.fill(0xFF); }
|
||||
CardRawFile::CardRawFile() { m_ch.raw.fill(0xFF); }
|
||||
|
||||
Card::Card(Card&& other) {
|
||||
CardRawFile::CardRawFile(CardRawFile&& other) {
|
||||
m_ch.raw = other.m_ch.raw;
|
||||
m_filename = std::move(other.m_filename);
|
||||
m_fileHandle = std::move(other.m_fileHandle);
|
||||
@@ -44,7 +42,7 @@ Card::Card(Card&& other) {
|
||||
std::copy(std::cbegin(other.m_maker), std::cend(other.m_maker), std::begin(m_maker));
|
||||
}
|
||||
|
||||
Card& Card::operator=(Card&& other) {
|
||||
CardRawFile& CardRawFile::operator=(CardRawFile&& other) {
|
||||
close();
|
||||
|
||||
m_ch.raw = other.m_ch.raw;
|
||||
@@ -62,7 +60,7 @@ Card& Card::operator=(Card&& other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
ECardResult Card::_pumpOpen() {
|
||||
ECardResult CardRawFile::_pumpOpen() {
|
||||
if (m_opened)
|
||||
return ECardResult::READY;
|
||||
|
||||
@@ -102,7 +100,7 @@ ECardResult Card::_pumpOpen() {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
Card::Card(const char* game, const char* maker) {
|
||||
void CardRawFile::InitCard(const char* game, const char* maker) {
|
||||
m_ch.raw.fill(0xFF);
|
||||
|
||||
if (game != nullptr && std::strlen(game) == 4) {
|
||||
@@ -113,9 +111,9 @@ Card::Card(const char* game, const char* maker) {
|
||||
}
|
||||
}
|
||||
|
||||
Card::~Card() { close(); }
|
||||
CardRawFile::~CardRawFile() { CardRawFile::close(); }
|
||||
|
||||
ECardResult Card::openFile(const char* filename, FileHandle& handleOut) {
|
||||
ECardResult CardRawFile::openFile(const char* filename, FileHandle& handleOut) {
|
||||
const ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY) {
|
||||
return openRes;
|
||||
@@ -136,7 +134,7 @@ ECardResult Card::openFile(const char* filename, FileHandle& handleOut) {
|
||||
return ECardResult::FATAL_ERROR;
|
||||
}
|
||||
|
||||
ECardResult Card::openFile(uint32_t fileno, FileHandle& handleOut) {
|
||||
ECardResult CardRawFile::openFile(uint32_t fileno, FileHandle& handleOut) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -149,7 +147,7 @@ ECardResult Card::openFile(uint32_t fileno, FileHandle& handleOut) {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
void Card::_updateDirAndBat(const Directory& dir, const BlockAllocationTable& bat) {
|
||||
void CardRawFile::_updateDirAndBat(const Directory& dir, const BlockAllocationTable& bat) {
|
||||
m_currentDir = !m_currentDir;
|
||||
Directory& updateDir = m_dirs[m_currentDir];
|
||||
updateDir = dir;
|
||||
@@ -165,13 +163,13 @@ void Card::_updateDirAndBat(const Directory& dir, const BlockAllocationTable& ba
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void Card::_updateChecksum() {
|
||||
void CardRawFile::_updateChecksum() {
|
||||
m_ch._swapEndian();
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(m_ch.raw.data()), 0xFE, &m_ch.m_checksum, &m_ch.m_checksumInv);
|
||||
m_ch._swapEndian();
|
||||
}
|
||||
|
||||
File* Card::_fileFromHandle(const FileHandle& fh) const {
|
||||
File* CardRawFile::_fileFromHandle(const FileHandle& fh) const {
|
||||
if (!fh) {
|
||||
NullFileAccess();
|
||||
return nullptr;
|
||||
@@ -179,7 +177,7 @@ File* Card::_fileFromHandle(const FileHandle& fh) const {
|
||||
return const_cast<Directory&>(m_dirs[m_currentDir]).getFile(fh.idx);
|
||||
}
|
||||
|
||||
ECardResult Card::createFile(const char* filename, size_t size, FileHandle& handleOut) {
|
||||
ECardResult CardRawFile::createFile(const char* filename, size_t size, FileHandle& handleOut) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -215,12 +213,12 @@ ECardResult Card::createFile(const char* filename, size_t size, FileHandle& hand
|
||||
return ECardResult::FATAL_ERROR;
|
||||
}
|
||||
|
||||
ECardResult Card::closeFile(FileHandle& fh) {
|
||||
ECardResult CardRawFile::closeFile(FileHandle& fh) {
|
||||
fh.offset = 0;
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
FileHandle Card::firstFile() {
|
||||
FileHandle CardRawFile::firstFile() {
|
||||
const File* const f = m_dirs[m_currentDir].getFirstNonFreeFile(0, m_game, m_maker);
|
||||
|
||||
if (f == nullptr) {
|
||||
@@ -230,7 +228,7 @@ FileHandle Card::firstFile() {
|
||||
return FileHandle(m_dirs[m_currentDir].indexForFile(f));
|
||||
}
|
||||
|
||||
FileHandle Card::nextFile(const FileHandle& cur) {
|
||||
FileHandle CardRawFile::nextFile(const FileHandle& cur) {
|
||||
if (!cur) {
|
||||
NullFileAccess();
|
||||
return {};
|
||||
@@ -244,7 +242,7 @@ FileHandle Card::nextFile(const FileHandle& cur) {
|
||||
return FileHandle(m_dirs[m_currentDir].indexForFile(next));
|
||||
}
|
||||
|
||||
const char* Card::getFilename(const FileHandle& fh) const {
|
||||
const char* CardRawFile::getFilename(const FileHandle& fh) const {
|
||||
const File* const f = _fileFromHandle(fh);
|
||||
|
||||
if (f == nullptr) {
|
||||
@@ -254,7 +252,7 @@ const char* Card::getFilename(const FileHandle& fh) const {
|
||||
return f->m_filename;
|
||||
}
|
||||
|
||||
void Card::_deleteFile(File& f, BlockAllocationTable& bat) {
|
||||
void CardRawFile::_deleteFile(File& f, BlockAllocationTable& bat) {
|
||||
uint16_t block = f.m_firstBlock;
|
||||
while (block != 0xFFFF) {
|
||||
/* TODO: add a fragmentation check */
|
||||
@@ -265,7 +263,7 @@ void Card::_deleteFile(File& f, BlockAllocationTable& bat) {
|
||||
f = File();
|
||||
}
|
||||
|
||||
void Card::deleteFile(const FileHandle& fh) {
|
||||
void CardRawFile::deleteFile(const FileHandle& fh) {
|
||||
if (!fh) {
|
||||
NullFileAccess();
|
||||
return;
|
||||
@@ -276,7 +274,7 @@ void Card::deleteFile(const FileHandle& fh) {
|
||||
_updateDirAndBat(dir, bat);
|
||||
}
|
||||
|
||||
ECardResult Card::deleteFile(const char* filename) {
|
||||
ECardResult CardRawFile::deleteFile(const char* filename) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -292,7 +290,7 @@ ECardResult Card::deleteFile(const char* filename) {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult Card::deleteFile(uint32_t fileno) {
|
||||
ECardResult CardRawFile::deleteFile(uint32_t fileno) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -308,7 +306,7 @@ ECardResult Card::deleteFile(uint32_t fileno) {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult Card::renameFile(const char* oldName, const char* newName) {
|
||||
ECardResult CardRawFile::renameFile(const char* oldName, const char* newName) {
|
||||
const ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY) {
|
||||
return openRes;
|
||||
@@ -338,7 +336,7 @@ ECardResult Card::renameFile(const char* oldName, const char* newName) {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult Card::asyncWrite(FileHandle& fh, const void* buf, size_t size) {
|
||||
ECardResult CardRawFile::fileWrite(FileHandle& fh, const void* buf, size_t size) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -387,7 +385,7 @@ ECardResult Card::asyncWrite(FileHandle& fh, const void* buf, size_t size) {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult Card::asyncRead(FileHandle& fh, void* dst, size_t size) {
|
||||
ECardResult CardRawFile::fileRead(FileHandle& fh, void* dst, size_t size) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -435,7 +433,7 @@ ECardResult Card::asyncRead(FileHandle& fh, void* dst, size_t size) {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
void Card::seek(FileHandle& fh, int32_t pos, SeekOrigin whence) {
|
||||
void CardRawFile::seek(FileHandle& fh, int32_t pos, SeekOrigin whence) {
|
||||
if (!fh) {
|
||||
NullFileAccess();
|
||||
return;
|
||||
@@ -457,9 +455,9 @@ void Card::seek(FileHandle& fh, int32_t pos, SeekOrigin whence) {
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Card::tell(const FileHandle& fh) const { return fh.offset; }
|
||||
int32_t CardRawFile::tell(const FileHandle& fh) const { return fh.offset; }
|
||||
|
||||
void Card::setPublic(const FileHandle& fh, bool pub) {
|
||||
void CardRawFile::setPublic(const FileHandle& fh, bool pub) {
|
||||
File* file = _fileFromHandle(fh);
|
||||
if (!file)
|
||||
return;
|
||||
@@ -470,7 +468,7 @@ void Card::setPublic(const FileHandle& fh, bool pub) {
|
||||
file->m_permissions &= ~EPermissions::Public;
|
||||
}
|
||||
|
||||
bool Card::isPublic(const FileHandle& fh) const {
|
||||
bool CardRawFile::isPublic(const FileHandle& fh) const {
|
||||
File* file = _fileFromHandle(fh);
|
||||
if (!file)
|
||||
return false;
|
||||
@@ -478,7 +476,7 @@ bool Card::isPublic(const FileHandle& fh) const {
|
||||
return static_cast<bool>(file->m_permissions & EPermissions::Public);
|
||||
}
|
||||
|
||||
void Card::setCanCopy(const FileHandle& fh, bool copy) const {
|
||||
void CardRawFile::setCanCopy(const FileHandle& fh, bool copy) const {
|
||||
File* file = _fileFromHandle(fh);
|
||||
if (!file)
|
||||
return;
|
||||
@@ -489,7 +487,7 @@ void Card::setCanCopy(const FileHandle& fh, bool copy) const {
|
||||
file->m_permissions |= EPermissions::NoCopy;
|
||||
}
|
||||
|
||||
bool Card::canCopy(const FileHandle& fh) const {
|
||||
bool CardRawFile::canCopy(const FileHandle& fh) const {
|
||||
File* file = _fileFromHandle(fh);
|
||||
if (!file)
|
||||
return false;
|
||||
@@ -497,7 +495,7 @@ bool Card::canCopy(const FileHandle& fh) const {
|
||||
return !static_cast<bool>(file->m_permissions & EPermissions::NoCopy);
|
||||
}
|
||||
|
||||
void Card::setCanMove(const FileHandle& fh, bool move) {
|
||||
void CardRawFile::setCanMove(const FileHandle& fh, bool move) {
|
||||
File* file = _fileFromHandle(fh);
|
||||
if (!file)
|
||||
return;
|
||||
@@ -508,7 +506,7 @@ void Card::setCanMove(const FileHandle& fh, bool move) {
|
||||
file->m_permissions |= EPermissions::NoMove;
|
||||
}
|
||||
|
||||
bool Card::canMove(const FileHandle& fh) const {
|
||||
bool CardRawFile::canMove(const FileHandle& fh) const {
|
||||
File* file = _fileFromHandle(fh);
|
||||
if (!file)
|
||||
return false;
|
||||
@@ -516,42 +514,7 @@ bool Card::canMove(const FileHandle& fh) const {
|
||||
return !static_cast<bool>(file->m_permissions & EPermissions::NoMove);
|
||||
}
|
||||
|
||||
static uint32_t BannerSize(EImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
default:
|
||||
case EImageFormat::None:
|
||||
return 0;
|
||||
case EImageFormat::C8:
|
||||
return 3584;
|
||||
case EImageFormat::RGB5A3:
|
||||
return 6144;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t IconSize(EImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
default:
|
||||
case EImageFormat::None:
|
||||
return 0;
|
||||
case EImageFormat::C8:
|
||||
return 1024;
|
||||
case EImageFormat::RGB5A3:
|
||||
return 2048;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t TlutSize(EImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
default:
|
||||
case EImageFormat::None:
|
||||
case EImageFormat::RGB5A3:
|
||||
return 0;
|
||||
case EImageFormat::C8:
|
||||
return 512;
|
||||
}
|
||||
}
|
||||
|
||||
ECardResult Card::getStatus(const FileHandle& fh, CardStat& statOut) const {
|
||||
ECardResult CardRawFile::getStatus(const FileHandle& fh, CardStat& statOut) const {
|
||||
if (!fh) {
|
||||
NullFileAccess();
|
||||
return ECardResult::NOFILE;
|
||||
@@ -559,8 +522,8 @@ ECardResult Card::getStatus(const FileHandle& fh, CardStat& statOut) const {
|
||||
return getStatus(fh.idx, statOut);
|
||||
}
|
||||
|
||||
ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
ECardResult openRes = const_cast<Card*>(this)->_pumpOpen();
|
||||
ECardResult CardRawFile::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
ECardResult openRes = const_cast<CardRawFile*>(this)->_pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
|
||||
@@ -612,7 +575,7 @@ ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
return ECardResult::READY;
|
||||
}
|
||||
|
||||
ECardResult Card::setStatus(const FileHandle& fh, const CardStat& stat) {
|
||||
ECardResult CardRawFile::setStatus(const FileHandle& fh, const CardStat& stat) {
|
||||
if (!fh) {
|
||||
NullFileAccess();
|
||||
return ECardResult::NOFILE;
|
||||
@@ -620,7 +583,7 @@ ECardResult Card::setStatus(const FileHandle& fh, const CardStat& stat) {
|
||||
return setStatus(fh.idx, stat);
|
||||
}
|
||||
|
||||
ECardResult Card::setStatus(uint32_t fileNo, const CardStat& stat) {
|
||||
ECardResult CardRawFile::setStatus(uint32_t fileNo, const CardStat& stat) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
@@ -701,7 +664,7 @@ bool Card::moveFileTo(FileHandle& fh, Card& dest)
|
||||
}
|
||||
#endif
|
||||
|
||||
void Card::setCurrentGame(const char* game) {
|
||||
void CardRawFile::setCurrentGame(const char* game) {
|
||||
if (game == nullptr) {
|
||||
std::memset(m_game, 0, sizeof(m_game));
|
||||
return;
|
||||
@@ -715,7 +678,7 @@ void Card::setCurrentGame(const char* game) {
|
||||
std::memcpy(m_game, game, copy_amount);
|
||||
}
|
||||
|
||||
const uint8_t* Card::getCurrentGame() const {
|
||||
const uint8_t* CardRawFile::getCurrentGame() const {
|
||||
if (std::strlen(m_game) == sizeof(m_game) - 1) {
|
||||
return reinterpret_cast<const uint8_t*>(m_game);
|
||||
}
|
||||
@@ -723,7 +686,7 @@ const uint8_t* Card::getCurrentGame() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Card::setCurrentMaker(const char* maker) {
|
||||
void CardRawFile::setCurrentMaker(const char* maker) {
|
||||
if (maker == nullptr) {
|
||||
std::memset(m_maker, 0, sizeof(m_maker));
|
||||
return;
|
||||
@@ -737,7 +700,7 @@ void Card::setCurrentMaker(const char* maker) {
|
||||
std::memcpy(m_maker, maker, copy_amount);
|
||||
}
|
||||
|
||||
const uint8_t* Card::getCurrentMaker() const {
|
||||
const uint8_t* CardRawFile::getCurrentMaker() const {
|
||||
if (std::strlen(m_maker) == sizeof(m_maker) - 1) {
|
||||
return reinterpret_cast<const uint8_t*>(m_maker);
|
||||
}
|
||||
@@ -745,7 +708,7 @@ const uint8_t* Card::getCurrentMaker() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Card::getSerial(uint64_t& serial) {
|
||||
void CardRawFile::getSerial(uint64_t& serial) {
|
||||
m_ch._swapEndian();
|
||||
|
||||
std::array<uint32_t, 8> serialBuf{};
|
||||
@@ -758,19 +721,19 @@ void Card::getSerial(uint64_t& serial) {
|
||||
m_ch._swapEndian();
|
||||
}
|
||||
|
||||
void Card::getChecksum(uint16_t& checksum, uint16_t& inverse) const {
|
||||
void CardRawFile::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) const {
|
||||
void CardRawFile::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const {
|
||||
bytesNotUsed = m_bats[m_currentBat].numFreeBlocks() * 0x2000;
|
||||
filesNotUsed = m_dirs[m_currentDir].numFreeFiles();
|
||||
}
|
||||
|
||||
void Card::getEncoding(uint16_t& encoding) const { encoding = m_ch.m_encoding; }
|
||||
void CardRawFile::getEncoding(uint16_t& encoding) const { encoding = m_ch.m_encoding; }
|
||||
|
||||
void Card::format(ECardSlot id, ECardSize size, EEncoding encoding) {
|
||||
void CardRawFile::format(ECardSlot id, ECardSize size, EEncoding encoding) {
|
||||
m_ch.raw.fill(0xFF);
|
||||
|
||||
uint64_t rand = static_cast<uint64_t>(getGCTime());
|
||||
@@ -827,7 +790,7 @@ void Card::format(ECardSlot id, ECardSize size, EEncoding encoding) {
|
||||
}
|
||||
}
|
||||
|
||||
ProbeResults Card::probeCardFile(std::string_view filename) {
|
||||
ProbeResults CardRawFile::probeCardFile(std::string_view filename) {
|
||||
std::filesystem::path path(filename);
|
||||
if (!std::filesystem::exists(path))
|
||||
return {ECardResult::NOCARD, 0, 0};
|
||||
@@ -835,7 +798,7 @@ ProbeResults Card::probeCardFile(std::string_view filename) {
|
||||
BlockSize};
|
||||
}
|
||||
|
||||
void Card::commit() {
|
||||
void CardRawFile::commit() {
|
||||
if (!m_dirty)
|
||||
return;
|
||||
if (m_fileHandle) {
|
||||
@@ -862,7 +825,7 @@ void Card::commit() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Card::open(std::string_view filepath) {
|
||||
bool CardRawFile::open(std::string_view filepath) {
|
||||
m_opened = false;
|
||||
m_filename = filepath;
|
||||
m_fileHandle = FileIO(m_filename);
|
||||
@@ -882,7 +845,7 @@ bool Card::open(std::string_view filepath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Card::close() {
|
||||
void CardRawFile::close() {
|
||||
m_opened = false;
|
||||
if (m_fileHandle) {
|
||||
commit();
|
||||
@@ -890,19 +853,19 @@ void Card::close() {
|
||||
}
|
||||
}
|
||||
|
||||
ECardResult Card::getError() const {
|
||||
ECardResult CardRawFile::getError() const {
|
||||
if (!m_fileHandle)
|
||||
return ECardResult::NOCARD;
|
||||
|
||||
ECardResult openRes = const_cast<Card*>(this)->_pumpOpen();
|
||||
ECardResult openRes = const_cast<CardRawFile*>(this)->_pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
return openRes;
|
||||
|
||||
uint16_t ckSum, ckSumInv;
|
||||
const_cast<Card&>(*this).m_ch._swapEndian();
|
||||
const_cast<CardRawFile&>(*this).m_ch._swapEndian();
|
||||
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();
|
||||
const_cast<CardRawFile&>(*this).m_ch._swapEndian();
|
||||
|
||||
if (!res)
|
||||
return ECardResult::BROKEN;
|
||||
@@ -10,75 +10,12 @@
|
||||
#include "File.hpp"
|
||||
#include "Util.hpp"
|
||||
|
||||
#define CARD_FILENAME_MAX 32
|
||||
#define CARD_ICON_MAX 8
|
||||
#include "CommonData.h"
|
||||
#include "ICard.hpp"
|
||||
|
||||
namespace aurora::card {
|
||||
|
||||
class FileHandle {
|
||||
friend class Card;
|
||||
uint32_t idx = UINT32_MAX;
|
||||
int32_t offset = 0;
|
||||
explicit FileHandle(uint32_t idx) : idx(idx) {}
|
||||
|
||||
public:
|
||||
FileHandle() = default;
|
||||
explicit FileHandle(uint32_t idx, int32_t offset) : idx(idx), offset(offset) {}
|
||||
|
||||
uint32_t getFileNo() const { return idx; }
|
||||
uint32_t getOffset() const { return offset; }
|
||||
explicit operator bool() const { return getFileNo() != UINT32_MAX; }
|
||||
};
|
||||
|
||||
struct ProbeResults {
|
||||
ECardResult x0_error;
|
||||
uint32_t x4_cardSize; /* in megabits */
|
||||
uint32_t x8_sectorSize; /* in bytes */
|
||||
};
|
||||
|
||||
struct CardStat {
|
||||
/* read-only (Set by Card::getStatus) */
|
||||
char x0_fileName[CARD_FILENAME_MAX];
|
||||
uint32_t x20_length;
|
||||
uint32_t x24_time; /* seconds since 01/01/2000 midnight */
|
||||
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;
|
||||
uint8_t x2f___padding;
|
||||
uint32_t x30_iconAddr; /* offset to the banner, bannerTlut, icon, iconTlut data set. */
|
||||
uint16_t x34_iconFormat;
|
||||
uint16_t x36_iconSpeed;
|
||||
uint32_t x38_commentAddr; /* offset to the pair of 32 byte character strings. */
|
||||
|
||||
/* read-only (Set by Card::getStatus) */
|
||||
uint32_t x3c_offsetBanner;
|
||||
uint32_t x40_offsetBannerTlut;
|
||||
std::array<uint32_t, CARD_ICON_MAX> x44_offsetIcon;
|
||||
uint32_t x64_offsetIconTlut;
|
||||
uint32_t x68_offsetData;
|
||||
|
||||
uint32_t GetFileLength() const { return x20_length; }
|
||||
uint32_t GetTime() const { return x24_time; }
|
||||
EImageFormat GetBannerFormat() const { return EImageFormat(x2e_bannerFormat & 0x3); }
|
||||
void SetBannerFormat(EImageFormat fmt) { x2e_bannerFormat = (x2e_bannerFormat & ~0x3) | uint8_t(fmt); }
|
||||
EImageFormat GetIconFormat(int idx) const { return EImageFormat((x34_iconFormat >> (idx * 2)) & 0x3); }
|
||||
void SetIconFormat(EImageFormat fmt, int idx) {
|
||||
x34_iconFormat &= ~(0x3 << (idx * 2));
|
||||
x34_iconFormat |= uint16_t(fmt) << (idx * 2);
|
||||
}
|
||||
void SetIconSpeed(EAnimationSpeed sp, int idx) {
|
||||
x36_iconSpeed &= ~(0x3 << (idx * 2));
|
||||
x36_iconSpeed |= uint16_t(sp) << (idx * 2);
|
||||
}
|
||||
uint32_t GetIconAddr() const { return x30_iconAddr; }
|
||||
void SetIconAddr(uint32_t addr) { x30_iconAddr = addr; }
|
||||
uint32_t GetCommentAddr() const { return x38_commentAddr; }
|
||||
void SetCommentAddr(uint32_t addr) { x38_commentAddr = addr; }
|
||||
};
|
||||
|
||||
class Card {
|
||||
class CardRawFile : public ICard {
|
||||
#pragma pack(push, 4)
|
||||
struct CardHeader {
|
||||
union {
|
||||
@@ -128,11 +65,11 @@ class Card {
|
||||
ECardResult _pumpOpen();
|
||||
|
||||
public:
|
||||
Card();
|
||||
Card(const Card& other) = delete;
|
||||
Card& operator=(const Card& other) = delete;
|
||||
Card(Card&& other);
|
||||
Card& operator=(Card&& other);
|
||||
CardRawFile();
|
||||
CardRawFile(const CardRawFile& other) = delete;
|
||||
CardRawFile& operator=(const CardRawFile& other) = delete;
|
||||
CardRawFile(CardRawFile&& other);
|
||||
CardRawFile& operator=(CardRawFile&& other);
|
||||
|
||||
/**
|
||||
* @brief Card
|
||||
@@ -140,8 +77,9 @@ public:
|
||||
* @param game The game code.
|
||||
* @param maker The maker code.
|
||||
*/
|
||||
explicit Card(const char* game = nullptr, const char* maker = nullptr);
|
||||
~Card();
|
||||
void InitCard(const char* game = nullptr, const char* maker = nullptr) override;
|
||||
|
||||
~CardRawFile() override;
|
||||
|
||||
/**
|
||||
* @brief openFile
|
||||
@@ -151,7 +89,7 @@ public:
|
||||
*
|
||||
* @return A result indicating the error status of the operation.
|
||||
*/
|
||||
ECardResult openFile(const char* filename, FileHandle& handleOut);
|
||||
ECardResult openFile(const char* filename, FileHandle& handleOut) override;
|
||||
|
||||
/**
|
||||
* @brief openFile
|
||||
@@ -161,7 +99,7 @@ public:
|
||||
*
|
||||
* @return A result indicating the error status of the operation.
|
||||
*/
|
||||
ECardResult openFile(uint32_t fileno, FileHandle& handleOut);
|
||||
ECardResult openFile(uint32_t fileno, FileHandle& handleOut) override;
|
||||
|
||||
/**
|
||||
* @brief createFile
|
||||
@@ -171,7 +109,7 @@ public:
|
||||
*
|
||||
* @return A result indicating the error status of the operation.
|
||||
*/
|
||||
ECardResult createFile(const char* filename, size_t size, FileHandle& handleOut);
|
||||
ECardResult createFile(const char* filename, size_t size, FileHandle& handleOut) override;
|
||||
|
||||
/**
|
||||
* @brief closeFile
|
||||
@@ -180,7 +118,7 @@ public:
|
||||
*
|
||||
* @return READY
|
||||
*/
|
||||
ECardResult closeFile(FileHandle& fh);
|
||||
ECardResult closeFile(FileHandle& fh) override;
|
||||
|
||||
/**
|
||||
* @brief firstFile
|
||||
@@ -215,21 +153,21 @@ public:
|
||||
*
|
||||
* @param fh File handle to delete.
|
||||
*/
|
||||
void deleteFile(const FileHandle& fh);
|
||||
void deleteFile(const FileHandle& fh) override;
|
||||
|
||||
/**
|
||||
* @brief deleteFile
|
||||
*
|
||||
* @param filename The name of the file to delete.
|
||||
*/
|
||||
ECardResult deleteFile(const char* filename);
|
||||
ECardResult deleteFile(const char* filename) override;
|
||||
|
||||
/**
|
||||
* @brief deleteFile
|
||||
*
|
||||
* @param fileno The file number indicating the file to delete.
|
||||
*/
|
||||
ECardResult deleteFile(uint32_t fileno);
|
||||
ECardResult deleteFile(uint32_t fileno) override;
|
||||
|
||||
/**
|
||||
* @brief renameFile
|
||||
@@ -237,7 +175,7 @@ public:
|
||||
* @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);
|
||||
ECardResult renameFile(const char* oldName, const char* newName) override;
|
||||
|
||||
/**
|
||||
* @brief write
|
||||
@@ -246,7 +184,7 @@ public:
|
||||
* @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);
|
||||
ECardResult fileWrite(FileHandle& fh, const void* buf, size_t size) override;
|
||||
|
||||
/**
|
||||
* @brief read
|
||||
@@ -255,7 +193,7 @@ public:
|
||||
* @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);
|
||||
ECardResult fileRead(FileHandle& fh, void* dst, size_t size) override;
|
||||
|
||||
/**
|
||||
* @brief seek
|
||||
@@ -264,7 +202,7 @@ public:
|
||||
* @param pos The position to seek to.
|
||||
* @param whence The origin to seek relative to.
|
||||
*/
|
||||
void seek(FileHandle& fh, int32_t pos, SeekOrigin whence);
|
||||
void seek(FileHandle& fh, int32_t pos, SeekOrigin whence) override;
|
||||
|
||||
/**
|
||||
* @brief Returns the current offset of the specified file.
|
||||
@@ -334,7 +272,7 @@ public:
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult getStatus(const FileHandle& fh, CardStat& statOut) const;
|
||||
ECardResult getStatus(const FileHandle& fh, CardStat& statOut) const override;
|
||||
|
||||
/**
|
||||
* @brief getStatus
|
||||
@@ -344,7 +282,7 @@ public:
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult getStatus(uint32_t fileNo, CardStat& statOut) const;
|
||||
ECardResult getStatus(uint32_t fileNo, CardStat& statOut) const override;
|
||||
|
||||
/**
|
||||
* @brief setStatus
|
||||
@@ -354,7 +292,7 @@ public:
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult setStatus(const FileHandle& fh, const CardStat& stat);
|
||||
ECardResult setStatus(const FileHandle& fh, const CardStat& stat) override;
|
||||
|
||||
/**
|
||||
* @brief setStatus
|
||||
@@ -364,7 +302,7 @@ public:
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult setStatus(uint32_t fileNo, const CardStat& stat);
|
||||
ECardResult setStatus(uint32_t fileNo, const CardStat& stat) override;
|
||||
|
||||
#if 0 // TODO: Async-friendly implementations
|
||||
/**
|
||||
@@ -373,7 +311,7 @@ public:
|
||||
* @param dest The destination Card instance
|
||||
* @return True if successful, false otherwise
|
||||
*/
|
||||
bool copyFileTo(FileHandle& fh, Card& dest);
|
||||
bool copyFileTo(FileHandle& fh, CardRawFile& dest);
|
||||
|
||||
/**
|
||||
* @brief moveFileTo
|
||||
@@ -381,7 +319,7 @@ public:
|
||||
* @param dest
|
||||
* @return
|
||||
*/
|
||||
bool moveFileTo(FileHandle& fh, Card& dest);
|
||||
bool moveFileTo(FileHandle& fh, CardRawFile& dest);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -391,14 +329,14 @@ public:
|
||||
*
|
||||
* @sa openFile
|
||||
*/
|
||||
void setCurrentGame(const char* game);
|
||||
void setCurrentGame(const char* game) override;
|
||||
|
||||
/**
|
||||
* @brief Returns the currently selected game.
|
||||
*
|
||||
* @return The selected game, or nullptr
|
||||
*/
|
||||
const uint8_t* getCurrentGame() const;
|
||||
const uint8_t* getCurrentGame() const override;
|
||||
|
||||
/**
|
||||
* @brief Sets the current maker, if not null any openFile requests will only return files that match this maker.
|
||||
@@ -407,21 +345,21 @@ public:
|
||||
*
|
||||
* @sa openFile
|
||||
*/
|
||||
void setCurrentMaker(const char* maker);
|
||||
void setCurrentMaker(const char* maker) override;
|
||||
|
||||
/**
|
||||
* @brief Returns the currently selected maker.
|
||||
*
|
||||
* @return The selected maker, or nullptr.
|
||||
*/
|
||||
const uint8_t* getCurrentMaker() const;
|
||||
const uint8_t* getCurrentMaker() const override;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the format assigned serial.
|
||||
*
|
||||
* @param[out] serial Out reference that will contain the serial number.
|
||||
*/
|
||||
void getSerial(uint64_t& serial);
|
||||
void getSerial(uint64_t& serial) override;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the checksum values of the Card system header.
|
||||
@@ -429,7 +367,7 @@ public:
|
||||
* @param checksum The checksum of the system header.
|
||||
* @param inverse The inverse checksum of the system header.
|
||||
*/
|
||||
void getChecksum(uint16_t& checksum, uint16_t& inverse) const;
|
||||
void getChecksum(uint16_t& checksum, uint16_t& inverse) const override;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the available storage and directory space.
|
||||
@@ -437,14 +375,14 @@ public:
|
||||
* @param bytesNotUsed Number of free bytes out.
|
||||
* @param filesNotUsed Number of free files out.
|
||||
*/
|
||||
void getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const;
|
||||
void getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const override;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the current file's encoding.
|
||||
*
|
||||
* @param encoding File encoding out.
|
||||
*/
|
||||
void getEncoding(uint16_t& encoding) const;
|
||||
void getEncoding(uint16_t& encoding) const override;
|
||||
|
||||
/**
|
||||
* @brief Formats the memory card and assigns a new serial.
|
||||
@@ -453,44 +391,44 @@ public:
|
||||
* @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);
|
||||
void format(ECardSlot deviceId, ECardSize size = ECardSize::Card2043Mb, EEncoding encoding = EEncoding::ASCII) override;
|
||||
|
||||
/**
|
||||
* @brief Returns basic stats about a card image without opening a handle.
|
||||
*
|
||||
* @return ProbeResults structure.
|
||||
*/
|
||||
static ProbeResults probeCardFile(std::string_view filename);
|
||||
ProbeResults probeCardFile(std::string_view filename) override;
|
||||
|
||||
/**
|
||||
* @brief Writes any changes to the Card instance immediately to disk. <br />
|
||||
* <b>Note:</b> <i>Under normal circumstances there is no need to call this function.</i>
|
||||
*/
|
||||
void commit();
|
||||
void commit() override;
|
||||
|
||||
/**
|
||||
* @brief Opens card image (does nothing if currently open path matches).
|
||||
*/
|
||||
bool open(std::string_view filepath);
|
||||
bool open(std::string_view filepath) override;
|
||||
|
||||
/**
|
||||
* @brief Commits changes to disk and closes host file.
|
||||
*/
|
||||
void close();
|
||||
void close() override;
|
||||
|
||||
/**
|
||||
* @brief Access host filename of card.
|
||||
*
|
||||
* @return A view to the card's filename.
|
||||
*/
|
||||
std::string_view cardFilename() const { return m_filename; }
|
||||
std::string_view cardFilename() const override { return m_filename; }
|
||||
|
||||
/**
|
||||
* @brief Gets card-scope error state.
|
||||
*
|
||||
* @return READY, BROKEN, or NOCARD
|
||||
*/
|
||||
ECardResult getError() const;
|
||||
ECardResult getError() const override;
|
||||
|
||||
/**
|
||||
* @return Whether or not the card is within a ready state.
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Util.hpp"
|
||||
#include "Constants.hpp"
|
||||
|
||||
namespace aurora::card {
|
||||
|
||||
class FileHandle {
|
||||
friend class CardRawFile;
|
||||
friend class CardGciFolder;
|
||||
uint32_t idx = UINT32_MAX;
|
||||
int32_t offset = 0;
|
||||
explicit FileHandle(uint32_t idx) : idx(idx) {}
|
||||
|
||||
public:
|
||||
FileHandle() = default;
|
||||
explicit FileHandle(uint32_t idx, int32_t offset) : idx(idx), offset(offset) {}
|
||||
|
||||
[[nodiscard]] uint32_t getFileNo() const { return idx; }
|
||||
[[nodiscard]] uint32_t getOffset() const { return offset; }
|
||||
explicit operator bool() const { return getFileNo() != UINT32_MAX; }
|
||||
};
|
||||
|
||||
struct ProbeResults {
|
||||
ECardResult x0_error;
|
||||
uint32_t x4_cardSize; /* in megabits */
|
||||
uint32_t x8_sectorSize; /* in bytes */
|
||||
};
|
||||
|
||||
struct CardStat {
|
||||
/* read-only (Set by Card::getStatus) */
|
||||
char x0_fileName[CARD_FILENAME_MAX];
|
||||
uint32_t x20_length;
|
||||
uint32_t x24_time; /* seconds since 01/01/2000 midnight */
|
||||
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;
|
||||
uint8_t x2f___padding;
|
||||
uint32_t x30_iconAddr; /* offset to the banner, bannerTlut, icon, iconTlut data set. */
|
||||
uint16_t x34_iconFormat;
|
||||
uint16_t x36_iconSpeed;
|
||||
uint32_t x38_commentAddr; /* offset to the pair of 32 byte character strings. */
|
||||
|
||||
/* read-only (Set by Card::getStatus) */
|
||||
uint32_t x3c_offsetBanner;
|
||||
uint32_t x40_offsetBannerTlut;
|
||||
std::array<uint32_t, CARD_ICON_MAX> x44_offsetIcon;
|
||||
uint32_t x64_offsetIconTlut;
|
||||
uint32_t x68_offsetData;
|
||||
|
||||
uint32_t GetFileLength() const { return x20_length; }
|
||||
uint32_t GetTime() const { return x24_time; }
|
||||
EImageFormat GetBannerFormat() const { return EImageFormat(x2e_bannerFormat & 0x3); }
|
||||
void SetBannerFormat(EImageFormat fmt) { x2e_bannerFormat = (x2e_bannerFormat & ~0x3) | uint8_t(fmt); }
|
||||
EImageFormat GetIconFormat(int idx) const { return EImageFormat((x34_iconFormat >> (idx * 2)) & 0x3); }
|
||||
void SetIconFormat(EImageFormat fmt, int idx) {
|
||||
x34_iconFormat &= ~(0x3 << (idx * 2));
|
||||
x34_iconFormat |= uint16_t(fmt) << (idx * 2);
|
||||
}
|
||||
void SetIconSpeed(EAnimationSpeed sp, int idx) {
|
||||
x36_iconSpeed &= ~(0x3 << (idx * 2));
|
||||
x36_iconSpeed |= uint16_t(sp) << (idx * 2);
|
||||
}
|
||||
uint32_t GetIconAddr() const { return x30_iconAddr; }
|
||||
void SetIconAddr(uint32_t addr) { x30_iconAddr = addr; }
|
||||
uint32_t GetCommentAddr() const { return x38_commentAddr; }
|
||||
void SetCommentAddr(uint32_t addr) { x38_commentAddr = addr; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#include "Util.hpp"
|
||||
|
||||
#define CARD_FILENAME_MAX 32
|
||||
#define CARD_ICON_MAX 8
|
||||
|
||||
namespace aurora::card {
|
||||
constexpr uint32_t BlockSize = 0x2000;
|
||||
constexpr uint32_t MaxFiles = 127;
|
||||
@@ -72,4 +75,40 @@ enum class EEncoding : uint16_t {
|
||||
ASCII, /**< Standard ASCII Encoding */
|
||||
SJIS /**< SJIS Encoding for japanese */
|
||||
};
|
||||
|
||||
constexpr uint32_t BannerSize(EImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
default:
|
||||
case EImageFormat::None:
|
||||
return 0;
|
||||
case EImageFormat::C8:
|
||||
return 3584;
|
||||
case EImageFormat::RGB5A3:
|
||||
return 6144;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint32_t IconSize(EImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
default:
|
||||
case EImageFormat::None:
|
||||
return 0;
|
||||
case EImageFormat::C8:
|
||||
return 1024;
|
||||
case EImageFormat::RGB5A3:
|
||||
return 2048;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint32_t TlutSize(EImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
default:
|
||||
case EImageFormat::None:
|
||||
case EImageFormat::RGB5A3:
|
||||
return 0;
|
||||
case EImageFormat::C8:
|
||||
return 512;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aurora::card
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace aurora::card {
|
||||
class Directory {
|
||||
friend class Card;
|
||||
friend class CardRawFile;
|
||||
|
||||
#pragma pack(push, 4)
|
||||
using RawData = std::array<uint8_t, BlockSize>;
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@ namespace aurora::card {
|
||||
class File {
|
||||
friend class IFileHandle;
|
||||
friend class Directory;
|
||||
friend class Card;
|
||||
friend class CardRawFile;
|
||||
friend class CardGciFolder;
|
||||
#pragma pack(push, 4)
|
||||
using RawData = std::array<uint8_t, 0x40>;
|
||||
union {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "FileIO.hpp"
|
||||
|
||||
#include <SDL3/SDL_iostream.h>
|
||||
#include <SDL3/SDL_filesystem.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
@@ -98,6 +99,23 @@ bool FileIO::fileWrite(const void* buf, size_t length, off_t offset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t FileIO::fileSize() const {
|
||||
SDL_PathInfo info;
|
||||
if (SDL_GetPathInfo(m_path.c_str(), &info)) {
|
||||
return info.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FileIO::deleteFile() {
|
||||
if (SDL_RemovePath(m_path.c_str())) {
|
||||
m_ready = false;
|
||||
m_path.clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FileIO::operator bool() const { return isReady(); }
|
||||
|
||||
} // namespace aurora::card
|
||||
|
||||
@@ -26,6 +26,8 @@ public:
|
||||
static SDL_IOStream* fileOpen(const std::string& path, const char* mode) {return path.empty() ? nullptr : SDL_IOFromFile(path.c_str(), mode);}
|
||||
bool fileRead(void* buf, size_t length, off_t offset);
|
||||
bool fileWrite(const void* buf, size_t length, off_t offset);
|
||||
size_t fileSize() const;
|
||||
bool deleteFile();
|
||||
explicit operator bool() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Util.hpp"
|
||||
|
||||
namespace aurora::card {
|
||||
class ICard {
|
||||
public:
|
||||
virtual ~ICard() = default;
|
||||
|
||||
virtual void InitCard(const char* game = nullptr, const char* maker = nullptr) = 0;
|
||||
|
||||
virtual ECardResult openFile(const char* filename, FileHandle& handleOut) = 0;
|
||||
virtual ECardResult openFile(uint32_t fileno, FileHandle& handleOut) = 0;
|
||||
virtual ECardResult createFile(const char* filename, size_t size, FileHandle& handleOut) = 0;
|
||||
virtual ECardResult closeFile(FileHandle& fh) = 0;
|
||||
virtual void deleteFile(const FileHandle& fh) = 0;
|
||||
virtual ECardResult deleteFile(const char* filename) = 0;
|
||||
virtual ECardResult deleteFile(uint32_t fileno) = 0;
|
||||
virtual ECardResult renameFile(const char* oldName, const char* newName) = 0;
|
||||
virtual ECardResult fileWrite(FileHandle& fh, const void* buf, size_t size) = 0;
|
||||
virtual ECardResult fileRead(FileHandle& fh, void* dst, size_t size) = 0;
|
||||
virtual void seek(FileHandle& fh, int32_t pos, SeekOrigin whence) = 0;
|
||||
virtual ECardResult getStatus(const FileHandle& fh, CardStat& statOut) const = 0;
|
||||
virtual ECardResult getStatus(uint32_t fileNo, CardStat& statOut) const = 0;
|
||||
virtual ECardResult setStatus(const FileHandle& fh, const CardStat& stat) = 0;
|
||||
virtual ECardResult setStatus(uint32_t fileNo, const CardStat& stat) = 0;
|
||||
virtual void setCurrentGame(const char* game) = 0;
|
||||
virtual const uint8_t* getCurrentGame() const = 0;
|
||||
virtual void setCurrentMaker(const char* maker) = 0;
|
||||
virtual const uint8_t* getCurrentMaker() const = 0;
|
||||
virtual void getSerial(uint64_t& serial) = 0;
|
||||
virtual void getChecksum(uint16_t& checksum, uint16_t& inverse) const = 0;
|
||||
virtual void getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const = 0;
|
||||
virtual void getEncoding(uint16_t& encoding) const = 0;
|
||||
virtual void format(ECardSlot deviceId, ECardSize size = ECardSize::Card2043Mb, EEncoding encoding = EEncoding::ASCII) = 0;
|
||||
virtual void commit() = 0;
|
||||
virtual bool open(std::string_view filepath) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual std::string_view cardFilename() const = 0;
|
||||
virtual ECardResult getError() const = 0;
|
||||
virtual ProbeResults probeCardFile(std::string_view filename) = 0;
|
||||
};
|
||||
}
|
||||
@@ -38,6 +38,8 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ROUND_UP_8192(val) (((val) + 8191) & ~8191)
|
||||
|
||||
namespace aurora::card {
|
||||
|
||||
uint64_t getGCTime();
|
||||
|
||||
+68
-49
@@ -4,27 +4,28 @@
|
||||
|
||||
#include "dolphin/types.h"
|
||||
|
||||
#include "../card/Card.hpp"
|
||||
#include "../card/CardRawFile.hpp"
|
||||
#include "../card/DolphinCardPath.hpp"
|
||||
#include "../logging.hpp"
|
||||
#include "../card/CardGciFolder.hpp"
|
||||
|
||||
namespace {
|
||||
aurora::Module Log("aurora::card");
|
||||
std::array<aurora::card::Card, 2> CardChannels = {{
|
||||
aurora::card::Card{nullptr, nullptr},
|
||||
aurora::card::Card{nullptr, nullptr},
|
||||
}};
|
||||
std::array<std::unique_ptr<aurora::card::ICard>, 2> CardChannels = {{}};
|
||||
std::array<std::string, 2> cardPaths;
|
||||
|
||||
constexpr uint16_t CARD_SECTOR_SIZE = 8192;
|
||||
#define GET_CARD(slot) &CardChannels[slot]
|
||||
#define CARD_READY(slot) std::filesystem::exists(CardChannels[slot].cardFilename())
|
||||
#define CARD_STUB Log.debug("{} is stubbed.", __FUNCTION__);
|
||||
|
||||
#define CARD_REGION "USA"
|
||||
|
||||
#define GET_CARD(slot) CardChannels[slot]
|
||||
#define CARD_READY(slot) (SelectedFileType == GciFolder ? true : std::filesystem::exists(CardChannels[slot]->cardFilename()))
|
||||
#define CARD_STUB Log.debug("{} is stubbed.", __FUNCTION__);
|
||||
|
||||
#define CARD_USE_GCI_FOLDER SelectedFileType == GciFolder
|
||||
|
||||
bool Initialized = false;
|
||||
bool UseGciFolder = false;
|
||||
CARDFileType SelectedFileType = CARDFileType::GciFolder;
|
||||
bool UseFastMode = false;
|
||||
|
||||
void CopyKabuFileHandleToDolphin(const s32 chan, const aurora::card::FileHandle& handle, CARDFileInfo* fileInfo) {
|
||||
@@ -41,11 +42,12 @@ std::string GetCardFullPath(const std::string& path, const aurora::card::ECardSl
|
||||
if (path.empty())
|
||||
return "";
|
||||
|
||||
if (UseGciFolder) {
|
||||
// TODO: probably not this
|
||||
return fmt::format("{}/{}/Card {}", path, CARD_REGION, slot == aurora::card::ECardSlot::SlotA ? "A" : "B");
|
||||
std::filesystem::path filePath(path);
|
||||
|
||||
if (CARD_USE_GCI_FOLDER) {
|
||||
return (filePath / CARD_REGION / (slot == aurora::card::ECardSlot::SlotA ? "Card A" : "Card B")).string();
|
||||
} else {
|
||||
return fmt::format("{}/MemoryCard{}.{}.raw", path, slot == aurora::card::ECardSlot::SlotA ? "A" : "B", CARD_REGION);
|
||||
return (filePath / fmt::format("MemoryCard{}.{}.raw", slot == aurora::card::ECardSlot::SlotA ? "A" : "B", CARD_REGION)).string();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -97,15 +99,15 @@ void CARDDetectDolphin(const s32 chan) {
|
||||
|
||||
if (chan == 0 || chan == 1) {
|
||||
cardPaths[chan] =
|
||||
aurora::card::ResolveDolphinCardPath(static_cast<aurora::card::ECardSlot>(chan), CARD_REGION, UseGciFolder);
|
||||
aurora::card::ResolveDolphinCardPath(static_cast<aurora::card::ECardSlot>(chan), CARD_REGION, CARD_USE_GCI_FOLDER);
|
||||
if (cardPaths[chan].empty()) {
|
||||
Log.error("Failed to detect Dolphin Card!");
|
||||
return;
|
||||
}
|
||||
Log.info("Detected Dolphin Card at: {}", cardPaths[chan]);
|
||||
} else {
|
||||
cardPaths[0] = aurora::card::ResolveDolphinCardPath(aurora::card::ECardSlot::SlotA, CARD_REGION, UseGciFolder);
|
||||
cardPaths[1] = aurora::card::ResolveDolphinCardPath(aurora::card::ECardSlot::SlotB, CARD_REGION, UseGciFolder);
|
||||
cardPaths[0] = aurora::card::ResolveDolphinCardPath(aurora::card::ECardSlot::SlotA, CARD_REGION, CARD_USE_GCI_FOLDER);
|
||||
cardPaths[1] = aurora::card::ResolveDolphinCardPath(aurora::card::ECardSlot::SlotB, CARD_REGION, CARD_USE_GCI_FOLDER);
|
||||
|
||||
if (cardPaths[0].empty() && cardPaths[1].empty()) {
|
||||
Log.error("Failed to detect Dolphin Card!");
|
||||
@@ -136,6 +138,10 @@ void CARDSetBasePath(const char* path, const s32 chan) {
|
||||
}
|
||||
}
|
||||
|
||||
void CARDSetLoadType(CARDFileType type) {
|
||||
SelectedFileType = type;
|
||||
}
|
||||
|
||||
void CARDInit(const char* game, const char* maker) {
|
||||
if (Initialized) {
|
||||
return;
|
||||
@@ -143,8 +149,17 @@ void CARDInit(const char* game, const char* maker) {
|
||||
Initialized = true;
|
||||
Log.info("CARD API Initialized BUILT <{} {}>", __DATE__, __TIME__);
|
||||
|
||||
CardChannels[0] = aurora::card::Card{game, maker};
|
||||
CardChannels[1] = aurora::card::Card{game, maker};
|
||||
for (int i = 0; i < CardChannels.size(); ++i) {
|
||||
switch (SelectedFileType) {
|
||||
case RawImage:
|
||||
CardChannels[i] = std::make_unique<aurora::card::CardRawFile>();
|
||||
break;
|
||||
case GciFolder:
|
||||
CardChannels[i] = std::make_unique<aurora::card::CardGciFolder>();
|
||||
break;
|
||||
}
|
||||
CardChannels[i]->InitCard(game, maker);
|
||||
}
|
||||
|
||||
std::string cardWorkingDir;
|
||||
if (aurora::g_config.configPath != nullptr)
|
||||
@@ -163,7 +178,7 @@ void CARDInit(const char* game, const char* maker) {
|
||||
const auto& curPath = cardPaths[i];
|
||||
|
||||
if (std::filesystem::exists(curPath)) {
|
||||
CardChannels[i].open(curPath);
|
||||
CardChannels[i]->open(curPath);
|
||||
loadedCard = true;
|
||||
Log.info("Loaded GC Card Image: {}", curPath);
|
||||
}
|
||||
@@ -171,10 +186,10 @@ void CARDInit(const char* game, const char* maker) {
|
||||
|
||||
// create a SlotA card if no cards were loaded
|
||||
if (!loadedCard) {
|
||||
CardChannels[0].open(cardPaths[0]);
|
||||
CardChannels[0].format(aurora::card::ECardSlot::SlotA);
|
||||
CardChannels[0].close();
|
||||
CardChannels[0].open(cardPaths[0]);
|
||||
CardChannels[0]->open(cardPaths[0]);
|
||||
CardChannels[0]->format(aurora::card::ECardSlot::SlotA);
|
||||
CardChannels[0]->close();
|
||||
CardChannels[0]->open(cardPaths[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,8 +198,8 @@ void CARDSetGameAndMaker(const s32 chan, const char* game, const char* maker) {
|
||||
return;
|
||||
}
|
||||
|
||||
CardChannels[chan].setCurrentGame(game);
|
||||
CardChannels[chan].setCurrentMaker(maker);
|
||||
CardChannels[chan]->setCurrentGame(game);
|
||||
CardChannels[chan]->setCurrentMaker(maker);
|
||||
}
|
||||
|
||||
// TODO: Investigate if this is necessary. Do some games throw an error if CARD's fast mode can't be use?
|
||||
@@ -203,7 +218,8 @@ s32 CARDCheck(const s32 chan) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
return static_cast<s32>(card->getError());
|
||||
}
|
||||
|
||||
@@ -214,7 +230,7 @@ s32 CARDCheckAsync(const s32 chan, const CARDCallback callback) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
const auto res = static_cast<s32>(card->getError());
|
||||
callback(chan, res);
|
||||
return static_cast<s32>(card->getError());
|
||||
@@ -227,7 +243,7 @@ s32 CARDCheckEx(const s32 chan, s32* xferBytes [[maybe_unused]]) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
return static_cast<s32>(card->getError());
|
||||
}
|
||||
@@ -236,7 +252,7 @@ s32 CARDCheckExAsync(const s32 chan, s32* xferBytes [[maybe_unused]], const CARD
|
||||
if (chan < 0 || chan >= 2) {
|
||||
return CARD_RESULT_FATAL_ERROR;
|
||||
}
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
const auto res = static_cast<s32>(card->getError());
|
||||
callback(chan, res);
|
||||
return static_cast<s32>(card->getError());
|
||||
@@ -248,7 +264,7 @@ s32 CARDCreate(const s32 chan, const char* fileName, const u32 size, CARDFileInf
|
||||
}
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
aurora::card::FileHandle handle;
|
||||
auto res = card->createFile(fileName, size, handle);
|
||||
@@ -278,7 +294,7 @@ s32 CARDDelete(const s32 chan, const char* fileName) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
const auto res = card->deleteFile(fileName);
|
||||
|
||||
if (res != aurora::card::ECardResult::READY) {
|
||||
@@ -306,7 +322,7 @@ s32 CARDFastDelete(const s32 chan, const s32 fileNo) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
const auto res = card->deleteFile(fileNo);
|
||||
if (res != aurora::card::ECardResult::READY) {
|
||||
Log.error("Failed to delete file at idx: {}", fileNo);
|
||||
@@ -333,7 +349,7 @@ s32 CARDFastOpen(const s32 chan, const s32 fileNo, CARDFileInfo* fileInfo) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
aurora::card::FileHandle handle;
|
||||
const auto res = card->openFile(fileNo, handle);
|
||||
@@ -352,7 +368,7 @@ s32 CARDFormat(s32 chan) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
card->format(static_cast<aurora::card::ECardSlot>(chan));
|
||||
card->commit();
|
||||
return CARD_RESULT_READY;
|
||||
@@ -374,7 +390,7 @@ s32 CARDFreeBlocks(const s32 chan, s32* byteNotUsed, s32* filesNotUsed) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
card->getFreeBlocks(*byteNotUsed, *filesNotUsed);
|
||||
return CARD_RESULT_READY;
|
||||
}
|
||||
@@ -395,7 +411,7 @@ s32 CARDGetEncoding(const s32 chan, u16* encode) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
card->getEncoding(*encode);
|
||||
return CARD_RESULT_READY;
|
||||
}
|
||||
@@ -416,7 +432,7 @@ s32 CARDGetResultCode(const s32 chan) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
return static_cast<s32>(card->getError());
|
||||
}
|
||||
|
||||
@@ -438,7 +454,7 @@ s32 CARDGetSerialNo(const s32 chan, u64* serialNo) {
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
card->getSerial(*serialNo);
|
||||
return CARD_RESULT_READY;
|
||||
}
|
||||
@@ -458,7 +474,7 @@ s32 CARDGetStatus(const s32 chan, s32 fileNo, CARDStat* stat) {
|
||||
}
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
aurora::card::CardStat kabuStat;
|
||||
const auto res = card->getStatus(fileNo, kabuStat);
|
||||
@@ -502,7 +518,7 @@ s32 CARDOpen(const s32 chan, const char* fileName, CARDFileInfo* fileInfo) {
|
||||
}
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
aurora::card::FileHandle handle;
|
||||
const auto res = card->openFile(fileName, handle);
|
||||
@@ -518,7 +534,9 @@ BOOL CARDProbe(const s32 chan) {
|
||||
if (chan < 0 || chan >= 2) {
|
||||
return CARD_RESULT_FATAL_ERROR;
|
||||
}
|
||||
aurora::card::ProbeResults probeData = aurora::card::Card::probeCardFile(cardPaths[chan]);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
aurora::card::ProbeResults probeData = card->probeCardFile(cardPaths[chan]);
|
||||
return probeData.x0_error != aurora::card::ECardResult::NOCARD ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
@@ -526,9 +544,10 @@ s32 CARDProbeEx(const s32 chan, s32* memSize, s32* sectorSize) {
|
||||
if (chan < 0 || chan >= 2) {
|
||||
return CARD_RESULT_FATAL_ERROR;
|
||||
}
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
// ReSharper disable once CppUseStructuredBinding
|
||||
const aurora::card::ProbeResults probeData = aurora::card::Card::probeCardFile(cardPaths[chan]);
|
||||
const aurora::card::ProbeResults probeData = card->probeCardFile(cardPaths[chan]);
|
||||
*memSize = probeData.x4_cardSize;
|
||||
*sectorSize = probeData.x8_sectorSize;
|
||||
|
||||
@@ -541,7 +560,7 @@ s32 CARDRename(const s32 chan, const char* oldName, const char* newName) {
|
||||
}
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
return static_cast<s32>(card->renameFile(oldName, newName));
|
||||
}
|
||||
@@ -580,7 +599,7 @@ s32 CARDSetStatus(const s32 chan, s32 fileNo, const CARDStat* stat) {
|
||||
}
|
||||
if (!CARD_READY(chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(chan);
|
||||
const auto& card = GET_CARD(chan);
|
||||
|
||||
aurora::card::CardStat kabuStat;
|
||||
CopyDolphinStatsToKabu(kabuStat, stat);
|
||||
@@ -639,7 +658,7 @@ s32 CARDClose(CARDFileInfo* fileInfo) {
|
||||
}
|
||||
if (!CARD_READY(fileInfo->chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(fileInfo->chan);
|
||||
const auto& card = GET_CARD(fileInfo->chan);
|
||||
|
||||
auto handle = CreateKabuFileHandleFromDolphin(fileInfo);
|
||||
const auto res = card->closeFile(handle);
|
||||
@@ -656,12 +675,12 @@ s32 CARDRead(const CARDFileInfo* fileInfo, void* addr, s32 length, const s32 off
|
||||
}
|
||||
if (!CARD_READY(fileInfo->chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(fileInfo->chan);
|
||||
const auto& card = GET_CARD(fileInfo->chan);
|
||||
|
||||
aurora::card::FileHandle handle = CreateKabuFileHandleFromDolphin(fileInfo);
|
||||
|
||||
card->seek(handle, offset, aurora::card::SeekOrigin::Begin);
|
||||
auto res = card->asyncRead(handle, addr, length);
|
||||
auto res = card->fileRead(handle, addr, length);
|
||||
|
||||
if (res != aurora::card::ECardResult::READY)
|
||||
Log.error("Failed to read {} bytes from card", length);
|
||||
@@ -682,12 +701,12 @@ s32 CARDWrite(const CARDFileInfo* fileInfo, const void* addr, const s32 length,
|
||||
}
|
||||
if (!CARD_READY(fileInfo->chan))
|
||||
return CARD_RESULT_NOCARD;
|
||||
const auto card = GET_CARD(fileInfo->chan);
|
||||
const auto& card = GET_CARD(fileInfo->chan);
|
||||
|
||||
aurora::card::FileHandle handle = CreateKabuFileHandleFromDolphin(fileInfo);
|
||||
|
||||
card->seek(handle, offset, aurora::card::SeekOrigin::Begin);
|
||||
auto res = card->asyncWrite(handle, addr, length);
|
||||
auto res = card->fileWrite(handle, addr, length);
|
||||
|
||||
if (res != aurora::card::ECardResult::READY) {
|
||||
Log.error("Failed to write {} bytes to card", length);
|
||||
|
||||
Reference in New Issue
Block a user