diff --git a/CMakeLists.txt b/CMakeLists.txt index ab401a3..3b7b729 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.0) project(kabufuda) -if(NOT TARGET urde) +if(NOT TARGET hecl) if(NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") endif() endif() @@ -20,6 +20,4 @@ add_library(kabufuda STATIC include/kabufuda/SRAM.hpp lib/kabufuda/SRAM.cpp include/kabufuda/WideStringConvert.hpp lib/kabufuda/WideStringConvert.cpp) -if (NOT TARGET hecl) - add_subdirectory(test) -endif() +add_subdirectory(test) diff --git a/Doxyfile b/Doxyfile index 002c603..74dbbcf 100644 --- a/Doxyfile +++ b/Doxyfile @@ -863,7 +863,9 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = include/utf8proc.h \ + include/kabufuda/Util.hpp \ + include/kabufuda/SRAM.hpp # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/include/kabufuda/BlockAllocationTable.hpp b/include/kabufuda/BlockAllocationTable.hpp index ba0fa6e..735b7dd 100644 --- a/include/kabufuda/BlockAllocationTable.hpp +++ b/include/kabufuda/BlockAllocationTable.hpp @@ -31,7 +31,7 @@ class BlockAllocationTable public: explicit BlockAllocationTable(uint32_t blockCount = (uint32_t(ECardSize::Card2043Mb) * MbitToBlocks)); BlockAllocationTable(uint8_t data[BlockSize]); - ~BlockAllocationTable() {} + ~BlockAllocationTable(); uint16_t getNextBlock(uint16_t block) const; uint16_t nextFreeBlock(uint16_t maxBlock, uint16_t startingBlock) const; diff --git a/include/kabufuda/Card.hpp b/include/kabufuda/Card.hpp index 25daba1..2dcc05d 100644 --- a/include/kabufuda/Card.hpp +++ b/include/kabufuda/Card.hpp @@ -59,9 +59,10 @@ class Card char m_game[5] = {'\0'}; char m_maker[3] = {'\0'}; - void swapEndian(); - void updateDirAndBat(); - void updateChecksum(); + void _swapEndian(); + void _updateDirAndBat(); + void _updateChecksum(); + File* _fileFromHandle(const std::unique_ptr& fh) const; public: Card(); Card(const Card& other); @@ -73,50 +74,162 @@ public: * @param filename */ std::unique_ptr openFile(const char* filename); + /** * @brief createFile * @param filename * @return */ std::unique_ptr createFile(const char* filename, size_t size); + + /** + * @brief deleteFile + * @param fh + */ void deleteFile(const std::unique_ptr& fh); + + /** + * @brief write + * @param fh + * @param buf + * @param size + */ void write(const std::unique_ptr& fh, const void* buf, size_t size); + + /** + * @brief read + * @param fh + * @param dst + * @param size + */ void read(const std::unique_ptr& fh, void* dst, size_t size); + + /** + * @brief seek + * @param fh + * @param pos + * @param whence + */ void seek(const std::unique_ptr& fh, int32_t pos, SeekOrigin whence); + + /** + * @brief Returns the current offset of the specified file + * @param fh The file to retrieve the offset from + * @return The offset or -1 if an invalid handle is passed + */ + int32_t tell(const std::unique_ptr& fh); + + /** + * @brief setPublic + * @param fh + * @param pub + */ + void setPublic(const std::unique_ptr& fh, bool pub); + + /** + * @brief isPublic + * @param fh + * @return + */ + bool isPublic(const std::unique_ptr& fh) const; + + /** + * @brief setCanCopy + * @param fh + * @param copy + */ + void setCanCopy(const std::unique_ptr& fh, bool copy) const; + + /** + * @brief canCopy + * @param fh + * @return + */ + bool canCopy(const std::unique_ptr& fh) const; + + /** + * @brief setCanMove + * @param fh + * @param move + */ + void setCanMove(const std::unique_ptr& fh, bool move); + + /** + * @brief canMove + * @param fh + * @return + */ + bool canMove(const std::unique_ptr& fh) const; + + void setBannerFormat(const std::unique_ptr& fh, EImageFormat fmt); + EImageFormat bannerFormat(const std::unique_ptr& fh) const; + void setIconAnimationType(const std::unique_ptr& fh, EAnimationType type); + EAnimationType iconAnimationType(const std::unique_ptr& fh) const; + void setIconFormat(const std::unique_ptr& fh, uint32_t idx, EImageFormat fmt); + EImageFormat iconFormat(const std::unique_ptr& fh, uint32_t idx) const; + void setIconSpeed(const std::unique_ptr& fh, uint32_t idx, EAnimationSpeed speed); + EAnimationSpeed iconSpeed(const std::unique_ptr& fh, uint32_t idx) const; + void setIconAddress(const std::unique_ptr& fh, uint32_t addr); + int32_t iconAddress(const std::unique_ptr& fh) const; + void setCommentAddress(const std::unique_ptr& fh, uint32_t addr); + int32_t commentAddress(const std::unique_ptr& fh) const; + + /** + * @brief Copies a file from the current Card instance to a specified Card instance + * @param fh The file to copy + * @param dest The destination Card instance + * @return True if successful, false otherwise + */ + bool copyFileTo(const std::unique_ptr&fh, Card& dest); + + /** + * @brief moveFileTo + * @param fh + * @param dest + * @return + */ + bool moveFileTo(const std::unique_ptr&fh, Card& dest); + /** * @brief Sets the current game, if not null any openFile requests will only return files that match this game * @param game The target game id, e.g "GM8E" * @sa openFile */ void setGame(const char* game); + /** * @brief Returns the currently selected game * @return The selected game, or nullptr */ const uint8_t* getGame() const; + /** * @brief Sets the current maker, if not null any openFile requests will only return files that match this maker * @param maker The target maker id, e.g "01" * @sa openFile */ void setMaker(const char* maker); + /** * @brief Returns the currently selected maker * @return The selected maker, or nullptr */ const uint8_t* getMaker() const; + /** * @brief Retrieves the format assigned serial in two 32bit parts * @param s0 * @param s1 */ void getSerial(uint32_t* s0, uint32_t* s1); + /** - * @brief Retrieves - * @param checksum - * @param inverse + * @brief Retrieves the checksum values of the Card system header + * @param checksum The checksum of the system header + * @param inverse The inverser checksum of the system header */ void getChecksum(uint16_t* checksum, uint16_t* inverse); + /** * @brief Formats the memory card and assigns a new serial * @param size The desired size of the file @sa ECardSize @@ -125,11 +238,15 @@ public: void format(EDeviceId deviceId, ECardSize size = ECardSize::Card2043Mb, EEncoding encoding = EEncoding::ASCII); /** - * @brief getSizeMbit - * @return + * @brief Returns the size of the file in Megabits from a file on disk, useful for determining filesize ahead of time. + * @return Size of file in Megabits */ static uint32_t getSizeMbitFromFile(const SystemString& filename); + /** + * @brief Writes any changes to the Card instance immediately to disk.
+ * Note: Under normal circumstances there is no need to call this function. + */ void commit(); operator bool() const; diff --git a/include/kabufuda/Constants.hpp b/include/kabufuda/Constants.hpp index e2e1282..dfa2aa3 100644 --- a/include/kabufuda/Constants.hpp +++ b/include/kabufuda/Constants.hpp @@ -2,6 +2,7 @@ #define __KABU_CONSTANTS_HPP__ #include +#include "Util.hpp" namespace kabufuda { @@ -16,13 +17,34 @@ uint32_t constexpr BATSize = 0xFFB; */ enum class EPermissions : uint8_t { + Public = (1 << 2), + NoCopy = (1 << 3), + NoMove = (1 << 4), + Global = (1 << 5), + Company = (1 << 6) +}; +ENABLE_BITWISE_ENUM(EPermissions) + + +enum class EImageFormat : uint8_t +{ + None, + C8, + RGB5A3, }; -/** - * @brief The EBannerFlags enum - */ -enum class EBannerFlags : uint8_t +enum class EAnimationType { + Loop = 0, + Bounce = 2, +}; + +enum class EAnimationSpeed +{ + End, + Fast, + Middle, + Slow, }; enum class SeekOrigin @@ -54,6 +76,11 @@ enum class ECardSize : uint16_t Card2043Mb = 0x80 }; +static constexpr uint32_t BannerWidth = 96; +static constexpr uint32_t BannerHeight = 64; +static constexpr uint32_t IconWidth = 32; +static constexpr uint32_t IconHeight = 32; + /** * @brief The EEncoding enum */ @@ -63,4 +90,5 @@ enum class EEncoding : uint16_t SJIS /**< SJIS Encoding for japanese */ }; } + #endif // __KABU_CONSTANTS_HPP__ diff --git a/include/kabufuda/Directory.hpp b/include/kabufuda/Directory.hpp index 3697ae2..d776848 100644 --- a/include/kabufuda/Directory.hpp +++ b/include/kabufuda/Directory.hpp @@ -32,7 +32,7 @@ public: Directory(uint8_t data[BlockSize]); Directory(const Directory& other); void operator=(const Directory& other); - ~Directory() {} + ~Directory(); File* getFirstFreeFile(const char* game, const char* maker, const char* filename); File* getFile(const char* game, const char* maker, const char* filename); diff --git a/include/kabufuda/File.hpp b/include/kabufuda/File.hpp index 52491dc..b5264a0 100644 --- a/include/kabufuda/File.hpp +++ b/include/kabufuda/File.hpp @@ -21,10 +21,10 @@ class File uint8_t m_bannerFlags; char m_filename[0x20]; uint32_t m_modifiedTime; - uint32_t m_imageOffset; + uint32_t m_iconAddress; uint16_t m_iconFmt; uint16_t m_animSpeed; - uint8_t m_permissions; + EPermissions m_permissions; int8_t m_copyCounter; uint16_t m_firstBlock; uint16_t m_blockCount; diff --git a/include/kabufuda/Util.hpp b/include/kabufuda/Util.hpp index fd8a135..eb8be8d 100644 --- a/include/kabufuda/Util.hpp +++ b/include/kabufuda/Util.hpp @@ -33,6 +33,38 @@ #undef bswap32 #undef bswap64 +#ifndef ENABLE_BITWISE_ENUM +#define ENABLE_BITWISE_ENUM(type)\ +inline type operator|(type a, type b)\ +{\ + using T = std::underlying_type_t;\ + return type(static_cast(a) | static_cast(b));\ +}\ +inline type operator&(type a, type b)\ +{\ + using T = std::underlying_type_t;\ + return type(static_cast(a) & static_cast(b));\ +}\ +inline type& operator|=(type& a, const type& b)\ +{\ + using T = std::underlying_type_t;\ + a = type(static_cast(a) | static_cast(b));\ + return a;\ +}\ +inline type& operator&=(type& a, const type& b)\ +{\ + using T = std::underlying_type_t;\ + a = type(static_cast(a) & static_cast(b));\ + return a;\ +}\ +inline type operator~(const type& key)\ +{\ + using T = std::underlying_type_t;\ + return type(~static_cast(key));\ +} +#endif + + namespace kabufuda { diff --git a/lib/kabufuda/BlockAllocationTable.cpp b/lib/kabufuda/BlockAllocationTable.cpp index f43e605..2faf746 100644 --- a/lib/kabufuda/BlockAllocationTable.cpp +++ b/lib/kabufuda/BlockAllocationTable.cpp @@ -41,6 +41,9 @@ BlockAllocationTable::BlockAllocationTable(uint32_t blockCount) updateChecksum(); } +BlockAllocationTable::~BlockAllocationTable() +{} + uint16_t BlockAllocationTable::getNextBlock(uint16_t block) const { if ((block < FSTBlocks) || (block > (BATSize - FSTBlocks))) diff --git a/lib/kabufuda/Card.cpp b/lib/kabufuda/Card.cpp index e2b3fb5..e3e95d1 100644 --- a/lib/kabufuda/Card.cpp +++ b/lib/kabufuda/Card.cpp @@ -33,7 +33,7 @@ FileHandle::~FileHandle() {} -void Card::swapEndian() +void Card::_swapEndian() { m_formatTime = SBig(m_formatTime); m_sramBias = SBig(m_sramBias); @@ -77,7 +77,7 @@ Card::Card(const SystemString& filename, const char* game, const char* maker) { fread(__raw, 1, BlockSize, m_fileHandle); m_maxBlock = m_sizeMb * MbitToBlocks; - swapEndian(); + _swapEndian(); fread(m_dir.__raw, 1, BlockSize, m_fileHandle); fread(m_dirBackup.__raw, 1, BlockSize, m_fileHandle); fread(m_bat.__raw, 1, BlockSize, m_fileHandle); @@ -144,7 +144,7 @@ std::unique_ptr Card::openFile(const char* filename) return nullptr; } -void Card::updateDirAndBat() +void Card::_updateDirAndBat() { Directory updateDir = *m_currentDir; updateDir.m_updateCounter++; @@ -157,16 +157,29 @@ void Card::updateDirAndBat() std::swap(m_currentBat, m_previousBat); } -void Card::updateChecksum() +void Card::_updateChecksum() { - swapEndian(); + _swapEndian(); calculateChecksumBE(reinterpret_cast(__raw), 0xFE, &m_checksum, &m_checksumInv); - swapEndian(); + _swapEndian(); +} + +File* Card::_fileFromHandle(const std::unique_ptr &fh) const +{ + if (!fh) + return nullptr; + + FileHandle* handle = dynamic_cast(fh.get()); + if (!handle) + return nullptr; + + File* file = m_currentDir->getFile(handle->game, handle->maker, handle->filename); + return file; } std::unique_ptr Card::createFile(const char* filename, size_t size) { - updateDirAndBat(); + _updateDirAndBat(); File* f = m_currentDir->getFirstFreeFile(m_game, m_maker, filename); uint16_t block = m_currentBat->allocateBlocks(uint16_t(size / BlockSize), m_maxBlock); if (f && block != 0xFFFF) @@ -183,7 +196,7 @@ std::unique_ptr Card::createFile(const char* filename, size_t size) void Card::deleteFile(const std::unique_ptr &fh) { - updateDirAndBat(); + _updateDirAndBat(); if (!fh) return; FileHandle* f = dynamic_cast(fh.get()); @@ -321,6 +334,236 @@ void Card::seek(const std::unique_ptr &fh, int32_t pos, SeekOrigin } } +int32_t Card::tell(const std::unique_ptr &fh) +{ + if (!fh) + return -1; + FileHandle* handle = dynamic_cast(fh.get()); + if (!handle) + return -1; + return handle->offset; +} + +void Card::setPublic(const std::unique_ptr& fh, bool pub) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + + if (pub) + file->m_permissions |= EPermissions::Public; + else + file->m_permissions &= ~EPermissions::Public; +} + +bool Card::isPublic(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return false; + + return bool(file->m_permissions & EPermissions::Public); +} + +void Card::setCanCopy(const std::unique_ptr &fh, bool copy) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + + if (copy) + file->m_permissions &= ~EPermissions::NoCopy; + else + file->m_permissions |= EPermissions::NoCopy; +} + +bool Card::canCopy(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return false; + + return !bool(file->m_permissions & EPermissions::NoCopy); +} + +void Card::setCanMove(const std::unique_ptr &fh, bool move) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + + if (move) + file->m_permissions &= ~EPermissions::NoMove; + else + file->m_permissions |= EPermissions::NoMove; +} + +bool Card::canMove(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return false; + + return !bool(file->m_permissions & EPermissions::NoMove); +} + +void Card::setBannerFormat(const std::unique_ptr& fh, EImageFormat fmt) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + file->m_bannerFlags = (file->m_bannerFlags & ~3) | (uint8_t(fmt)); +} + +EImageFormat Card::bannerFormat(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return EImageFormat::None; + return EImageFormat(file->m_bannerFlags & 3); +} + +void Card::setIconAnimationType(const std::unique_ptr &fh, EAnimationType type) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + file->m_bannerFlags = (file->m_bannerFlags & ~4) | uint8_t(type); +} + +EAnimationType Card::iconAnimationType(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return EAnimationType::Loop; + + return EAnimationType(file->m_bannerFlags & 4); +} + +void Card::setIconFormat(const std::unique_ptr &fh, uint32_t idx, EImageFormat fmt) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + + file->m_iconFmt = (file->m_iconFmt & ~(3 << (2 * idx))) | (uint16_t(fmt) << (2 * idx)); +} + +EImageFormat Card::iconFormat(const std::unique_ptr &fh, uint32_t idx) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return EImageFormat::None; + + return EImageFormat(file->m_iconFmt >> (2 * (idx)) & 3); +} + +void Card::setIconSpeed(const std::unique_ptr &fh, uint32_t idx, EAnimationSpeed speed) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + + file->m_animSpeed = (file->m_animSpeed & ~(3 << (2 * idx))) | (uint16_t(speed) << (2 * idx)); +} + +EAnimationSpeed Card::iconSpeed(const std::unique_ptr &fh, uint32_t idx) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return EAnimationSpeed::End; + + return EAnimationSpeed((file->m_animSpeed >> (2 * (idx))) & 3); +} + +void Card::setIconAddress(const std::unique_ptr &fh, uint32_t addr) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + file->m_iconAddress = addr; +} + +int32_t Card::iconAddress(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return -1; + return file->m_iconAddress; +} + +void Card::setCommentAddress(const std::unique_ptr &fh, uint32_t addr) +{ + File* file = _fileFromHandle(fh); + if (!file) + return; + file->m_commentAddr = addr; +} + +int32_t Card::commentAddress(const std::unique_ptr &fh) const +{ + File* file = _fileFromHandle(fh); + if (!file) + return -1; + return file->m_commentAddr; +} + +bool Card::copyFileTo(const std::unique_ptr &fh, Card &dest) +{ + if (!fh) + return false; + /* Do a self test to avoid adding a file to itself */ + if (this == &dest) + return false; + + /* Check to make sure dest does not already contain fh */ + if (dest._fileFromHandle(fh) != nullptr) + return false; + + /* Now to add fh */ + File* toCopy = _fileFromHandle(fh); + if (!toCopy) + return false; + + /* Try to allocate a new file */ + std::unique_ptr tmpHandle = dest.createFile(toCopy->m_filename, toCopy->m_blockCount * BlockSize); + if (!tmpHandle) + return false; + + /* Now copy the file information over */ + File* copyDest = dest._fileFromHandle(tmpHandle); + File copyTmp = *copyDest; + *copyDest = *toCopy; + copyDest->m_firstBlock = copyTmp.m_firstBlock; + copyDest->m_copyCounter++; + + /* Finally lets get the data copied over! */ + uint32_t len = toCopy->m_blockCount * BlockSize; + uint32_t oldPos = tell(fh); + seek(fh, 0, SeekOrigin::Begin); + while (len > 0) + { + uint8_t tmp[BlockSize]; + read(fh, tmp, BlockSize); + dest.write(tmpHandle, tmp, BlockSize); + len -= BlockSize; + } + + seek(fh, oldPos, SeekOrigin::Begin); + return true; +} + +bool Card::moveFileTo(const std::unique_ptr &fh, Card &dest) +{ + if (copyFileTo(fh, dest)) + { + deleteFile(fh); + return true; + } + + return false; +} + void Card::setGame(const char* game) { if (game == nullptr) @@ -399,7 +642,7 @@ void Card::format(EDeviceId id, ECardSize size, EEncoding encoding) m_sizeMb = uint16_t(size); m_maxBlock = m_sizeMb * MbitToBlocks; m_encoding = uint16_t(encoding); - updateChecksum(); + _updateChecksum(); m_dir = Directory(); m_dirBackup = m_dir; m_bat = BlockAllocationTable(uint32_t(size) * MbitToBlocks); @@ -416,9 +659,9 @@ void Card::format(EDeviceId id, ECardSize size, EEncoding encoding) if (m_fileHandle) { - swapEndian(); + _swapEndian(); fwrite(__raw, 1, BlockSize, m_fileHandle); - swapEndian(); + _swapEndian(); Directory tmpDir = m_dir; tmpDir.swapEndian(); fwrite(tmpDir.__raw, 1, BlockSize, m_fileHandle); @@ -453,9 +696,9 @@ void Card::commit() { rewind(m_fileHandle); - swapEndian(); + _swapEndian(); fwrite(__raw, 1, BlockSize, m_fileHandle); - swapEndian(); + _swapEndian(); Directory tmpDir = m_dir; tmpDir.updateChecksum(); tmpDir.swapEndian(); @@ -482,7 +725,7 @@ Card::operator bool() const uint16_t ckSum, ckSumInv; Card tmp = *this; - tmp.swapEndian(); + tmp._swapEndian(); calculateChecksumBE(reinterpret_cast(tmp.__raw), 0xFE, &ckSum, &ckSumInv); if (SBig(ckSum) != m_checksum || SBig(ckSumInv) != m_checksumInv) return false; diff --git a/lib/kabufuda/Directory.cpp b/lib/kabufuda/Directory.cpp index 255d202..318bf84 100644 --- a/lib/kabufuda/Directory.cpp +++ b/lib/kabufuda/Directory.cpp @@ -53,6 +53,10 @@ void Directory::operator=(const Directory& other) memcpy(__raw, other.__raw, BlockSize); } +Directory::~Directory() +{ +} + File* Directory::getFirstFreeFile(const char* game, const char* maker, const char* filename) { for (uint16_t i = 0 ; i < 127 ; i++) diff --git a/lib/kabufuda/File.cpp b/lib/kabufuda/File.cpp index ecd663f..ce2dc15 100644 --- a/lib/kabufuda/File.cpp +++ b/lib/kabufuda/File.cpp @@ -3,6 +3,11 @@ namespace kabufuda { +File::File() +{ + memset(__raw, 0xFF, 0x40); +} + File::File(char data[]) { memcpy(__raw, data, 0x40); @@ -10,7 +15,7 @@ File::File(char data[]) File::File(const char* filename) { - memset(__raw, 0xFF, 0x40); + memset(__raw, 0, 0x40); memset(m_filename, 0, 32); size_t len = strlen(filename); len = std::min(len, 32); @@ -19,7 +24,7 @@ File::File(const char* filename) void File::swapEndian() { m_modifiedTime = SBig(m_modifiedTime); - m_imageOffset = SBig(m_imageOffset); + m_iconAddress = SBig(m_iconAddress); m_iconFmt = SBig(m_iconFmt); m_animSpeed = SBig(m_animSpeed); m_firstBlock = SBig(m_firstBlock); @@ -27,9 +32,4 @@ void File::swapEndian() m_reserved2 = SBig(m_reserved2); m_commentAddr = SBig(m_commentAddr); } - -File::File() -{ - memset(__raw, 0xFF, 0x40); -} } diff --git a/test/main.cpp b/test/main.cpp index 45fa1a9..44671d0 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -6,16 +6,28 @@ int main() kabufuda::Card mc{_S("test.USA.raw"), "GM8E", "01"}; if (!mc) mc.format(kabufuda::EDeviceId::SlotA, kabufuda::ECardSize::Card2043Mb); + kabufuda::Card mc2{_S("test2.USA.raw"), "GM8E", "01"}; + if (!mc2) + mc2.format(kabufuda::EDeviceId::SlotA, kabufuda::ECardSize::Card2043Mb); + std::unique_ptr f = mc.openFile("MetroidPrime B"); if (!f) + { f = mc.createFile("MetroidPrime B", kabufuda::BlockSize); + mc.setPublic(f, true); + mc.setCanCopy(f, true); + mc.setCanMove(f, true); + mc.setIconAddress(f, mc.commentAddress(f) + 64); + } if (f) { + mc.setBannerFormat(f, kabufuda::EImageFormat::C8); + mc.setIconFormat(f, 0, kabufuda::EImageFormat::C8); + mc.setIconSpeed(f, 0, kabufuda::EAnimationSpeed::Middle); + const char* test = "Metroid Prime B is Cool"; size_t len = strlen(test); - uint8_t data[kabufuda::BlockSize] = {}; - mc.write(f, data, kabufuda::BlockSize); mc.seek(f, 0, kabufuda::SeekOrigin::Begin); mc.write(f, test, len + 1); uint16_t derp = 1234; @@ -24,7 +36,10 @@ int main() mc.seek(f, -2, kabufuda::SeekOrigin::Current); mc.read(f, &derp, 2); std::cout << derp << std::endl; - mc.deleteFile(f); + if (mc.copyFileTo(f, mc2)) + printf("Copy succeeded!\n"); + else + printf("Copy failed...\n"); } return 0; }