aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/fs/ds/ds-fs.cpp1736
-rw-r--r--backends/fs/ds/ds-fs.h356
-rw-r--r--backends/platform/ds/arm7/Makefile436
-rw-r--r--backends/platform/ds/arm7/source/main.cpp1130
-rw-r--r--backends/platform/ds/arm9/buildkeyboard.bat8
-rw-r--r--backends/platform/ds/arm9/lib/readme.txt2
-rw-r--r--backends/platform/ds/arm9/makefile522
-rw-r--r--backends/platform/ds/arm9/source/blitters.cpp272
-rw-r--r--backends/platform/ds/arm9/source/blitters.h62
-rw-r--r--backends/platform/ds/arm9/source/cdaudio.cpp968
-rw-r--r--backends/platform/ds/arm9/source/cdaudio.h74
-rw-r--r--backends/platform/ds/arm9/source/compressor/lz.cpp1078
-rw-r--r--backends/platform/ds/arm9/source/compressor/lz.h100
-rw-r--r--backends/platform/ds/arm9/source/console2.h124
-rw-r--r--backends/platform/ds/arm9/source/dsmain.cpp3888
-rw-r--r--backends/platform/ds/arm9/source/dsmain.h236
-rw-r--r--backends/platform/ds/arm9/source/dsoptions.cpp412
-rw-r--r--backends/platform/ds/arm9/source/dsoptions.h122
-rw-r--r--backends/platform/ds/arm9/source/fat/disc_io.c844
-rw-r--r--backends/platform/ds/arm9/source/fat/disc_io.h364
-rw-r--r--backends/platform/ds/arm9/source/fat/gba_nds_fat.c6660
-rw-r--r--backends/platform/ds/arm9/source/fat/gba_nds_fat.h888
-rw-r--r--backends/platform/ds/arm9/source/fat/io_efa2.c642
-rw-r--r--backends/platform/ds/arm9/source/fat/io_efa2.h56
-rw-r--r--backends/platform/ds/arm9/source/fat/io_fcsr.c658
-rw-r--r--backends/platform/ds/arm9/source/fat/io_fcsr.h48
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3cf.c734
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3cf.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3sd.c750
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3sd.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3sd_asm.s392
-rw-r--r--backends/platform/ds/arm9/source/fat/io_mpcf.c714
-rw-r--r--backends/platform/ds/arm9/source/fat/io_mpcf.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_nmmc.c716
-rw-r--r--backends/platform/ds/arm9/source/fat/io_nmmc.h54
-rw-r--r--backends/platform/ds/arm9/source/fat/io_sccf.c174
-rw-r--r--backends/platform/ds/arm9/source/fat/io_sccf.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_scsd.c186
-rw-r--r--backends/platform/ds/arm9/source/fat/io_scsd.h58
-rw-r--r--backends/platform/ds/arm9/source/fat/io_scsd_asm.s1042
-rw-r--r--backends/platform/ds/arm9/source/gbampsave.cpp372
-rw-r--r--backends/platform/ds/arm9/source/gbampsave.h164
-rw-r--r--backends/platform/ds/arm9/source/mad/readme.txt2
-rw-r--r--backends/platform/ds/arm9/source/osystem_ds.cpp968
-rw-r--r--backends/platform/ds/arm9/source/osystem_ds.h284
-rw-r--r--backends/platform/ds/arm9/source/portdefs.cpp84
-rw-r--r--backends/platform/ds/arm9/source/portdefs.h100
-rw-r--r--backends/platform/ds/arm9/source/ramsave.cpp918
-rw-r--r--backends/platform/ds/arm9/source/ramsave.h280
-rw-r--r--backends/platform/ds/arm9/source/scummconsole.c1122
-rw-r--r--backends/platform/ds/arm9/source/scummconsole.h124
-rw-r--r--backends/platform/ds/arm9/source/scummhelp.cpp172
-rw-r--r--backends/platform/ds/arm9/source/scummhelp.h64
-rw-r--r--backends/platform/ds/arm9/source/touchkeyboard.cpp626
-rw-r--r--backends/platform/ds/arm9/source/touchkeyboard.h202
-rw-r--r--backends/platform/ds/arm9/source/zipreader.cpp442
-rw-r--r--backends/platform/ds/arm9/source/zipreader.h162
-rw-r--r--backends/platform/ds/commoninclude/NDS/scummvm_ipc.h186
-rw-r--r--backends/platform/ds/makefile42
-rw-r--r--common/scummsys.h28
-rw-r--r--engines/scumm/dialogs.cpp9
-rw-r--r--engines/scumm/gfx.cpp15
-rw-r--r--engines/scumm/thumbnail.cpp2
-rw-r--r--gui/options.cpp2
-rw-r--r--sound/fmopl.cpp4
65 files changed, 33075 insertions, 5 deletions
diff --git a/backends/fs/ds/ds-fs.cpp b/backends/fs/ds/ds-fs.cpp
new file mode 100644
index 0000000000..8032267e83
--- /dev/null
+++ b/backends/fs/ds/ds-fs.cpp
@@ -0,0 +1,1736 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include "stdafx.h"
+#include "str.h"
+#include "fs.h"
+#include "common/util.h"
+#include <NDS/ARM9/console.h> //basic print funcionality
+#include "ds-fs.h"
+#include "dsmain.h"
+#include "gba_nds_fat.h"
+
+namespace DS {
+
+//////////////////////////////////////////////////////////////
+// DSFileSystemNode - Flash ROM file system using Zip files
+//////////////////////////////////////////////////////////////
+
+ZipFile* DSFileSystemNode::_zipFile = NULL;
+char currentDir[128];
+
+DSFileSystemNode::DSFileSystemNode() {
+ _displayName = "ds:/";
+ _path = "ds:/";
+ _isValid = true;
+ _isDirectory = true;
+ _path = "ds:/";
+
+/* if (!_archive) {
+ _archive = (GBFS_FILE *) find_first_gbfs_file(scummdata);
+ if (!_archive) consolePrintf("No GBFS archive found!\n");
+ }*/
+
+ if (!_zipFile) {
+ _zipFile = new ZipFile();
+ }
+}
+
+DSFileSystemNode::DSFileSystemNode(const String& path) {
+// consolePrintf("--%s ",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+
+
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if (path[r] == '\\') {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ _displayName = String(disp);
+ _path = path;
+// _isValid = true;
+// _isDirectory = false;
+
+
+
+ if (!strncmp(pathStr, "ds:/", 4)) {
+ pathStr += 4;
+ }
+
+
+ if (*pathStr == '\0') {
+ _isValid = true;
+ _isDirectory = true;
+ return;
+ }
+
+ _zipFile->setAllFilesVisible(true);
+ if (_zipFile->findFile(pathStr)) {
+ _isValid = true;
+ _isDirectory = _zipFile->isDirectory();
+ } else {
+ _isValid = false;
+ _isDirectory = false;
+ }
+ _zipFile->setAllFilesVisible(false);
+
+// consolePrintf("%s - Found: %d, Dir: %d\n", pathStr, _isValid, _isDirectory);
+}
+
+DSFileSystemNode::DSFileSystemNode(const String& path, bool isDir) {
+// consolePrintf("--%s ",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if (path[r] == '\\') {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ _displayName = String(disp);
+ _path = path;
+ _isValid = true;
+ _isDirectory = isDir;
+
+// consolePrintf("Found: %d, Dir: %d\n", _isValid, _isDirectory);
+}
+
+DSFileSystemNode::DSFileSystemNode(const DSFileSystemNode* node) {
+
+}
+
+AbstractFilesystemNode* DSFileSystemNode::parent() const {
+// consolePrintf("parent\n");
+ DSFileSystemNode *p;
+
+ if (_path != "ds:/") {
+ char *path = (char *) _path.c_str();
+ int lastSlash = 4;
+
+ for (int r = 4; r < (int) strlen((char *) path); r++) {
+ if (path[r] == '\\') {
+ lastSlash = r;
+ }
+ }
+
+ p = new DSFileSystemNode(String(path, lastSlash));
+ ((DSFileSystemNode *) (p))->_isDirectory = true;
+ } else {
+ p = new DSFileSystemNode();
+ }
+
+ return p;
+
+}
+
+
+AbstractFilesystemNode *DSFileSystemNode::child(const Common::String& name) const {
+ if (_path.lastChar() == '\\') {
+ return new DSFileSystemNode(_path + name);
+ } else {
+ return new DSFileSystemNode(_path + "\\" + name);
+ }
+
+ return NULL;
+}
+
+
+bool DSFileSystemNode::listDir(AbstractFSList &dirList, ListMode mode) const {
+// consolePrintf("Listdir\n");
+
+
+// consolePrintf("Directory\n");
+
+
+ char temp[128];
+ strcpy(temp, _path.c_str());
+
+// consolePrintf("This dir: %s\n", temp);
+
+ if ((temp[0] == 'd') && (temp[1] == 's') && (temp[2] == ':') && (temp[3] == '/')) {
+ if (strlen(temp) != 4) {
+ _zipFile->changeDirectory(&temp[4]);
+ } else {
+ _zipFile->changeToRoot();
+
+/* // This is the root dir, so add the RAM folder
+ DSFileSystemNode* dsfsn = new DSFileSystemNode("ds:/ram");
+ dsfsn->_isDirectory = true;
+ dirList->push_back(wrap(dsfsn));*/
+ }
+ } else {
+ _zipFile->changeDirectory(temp);
+ }
+
+
+
+ if (_zipFile->restartFile()) {
+ do {
+ char name[128];
+ _zipFile->getFileName(name);
+
+// consolePrintf("file: %s\n", name);
+ if ( (_zipFile->isDirectory() && ((mode == FilesystemNode::kListDirectoriesOnly) || (mode == FilesystemNode::kListAll)) )
+ || (!_zipFile->isDirectory() && ((mode == FilesystemNode::kListFilesOnly) || (mode == FilesystemNode::kListAll)) ) )
+ {
+ DSFileSystemNode* dsfsn = new DSFileSystemNode("ds:/" + String(name), _zipFile->isDirectory());
+ dsfsn->_isDirectory = _zipFile->isDirectory();
+ dirList.push_back((dsfsn));
+ }
+
+ } while (_zipFile->skipFile());
+ }
+
+ return true;
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// GBAMPFileSystemNode - File system using GBA Movie Player and CF card
+/////////////////////////////////////////////////////////////////////////
+
+GBAMPFileSystemNode::GBAMPFileSystemNode() {
+ _displayName = "mp:/";
+ _path = "mp:/";
+ _isValid = true;
+ _isDirectory = true;
+ _path = "mp:/";
+}
+
+GBAMPFileSystemNode::GBAMPFileSystemNode(const String& path) {
+// consolePrintf("'%s'",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if ((path[r] == '\\') || (path[r] == '/')) {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ char check[128];
+ int success;
+
+ memset(check, 0, 128);
+ if (strlen(pathStr) > 3) {
+ strcpy(check, pathStr + 3);
+ if (check[strlen(check) - 1] == '/') {
+ check[strlen(check) - 1] = 0;
+ }
+ success = FAT_FileExists(check);
+ } else {
+ success = FT_DIR;
+ }
+// consolePrintf("Path: %s (%d)\n", check, success);
+
+ _displayName = String(disp);
+ _path = path;
+ _isValid = success == FT_FILE;
+ _isDirectory = success == FT_DIR;
+}
+
+GBAMPFileSystemNode::GBAMPFileSystemNode(const String& path, bool isDirectory) {
+// consolePrintf("'%s'",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if ((path[r] == '\\') || (path[r] == '/')) {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ _displayName = String(disp);
+ _path = path;
+ _isValid = true;
+ _isDirectory = isDirectory;
+}
+
+
+GBAMPFileSystemNode::GBAMPFileSystemNode(const GBAMPFileSystemNode* node) {
+
+}
+
+
+AbstractFilesystemNode* GBAMPFileSystemNode::parent() const {
+// consolePrintf("parent\n");
+ GBAMPFileSystemNode *p;
+
+ if (_path != "mp:/") {
+ char *path = (char *) _path.c_str();
+ int lastSlash = 4;
+
+ for (int r = 4; r < (int) strlen((char *) path); r++) {
+ if (path[r] == '/') {
+ lastSlash = r;
+ }
+ }
+
+ p = new GBAMPFileSystemNode(String(path, lastSlash));
+ p->_isDirectory = true;
+ } else {
+ p = new GBAMPFileSystemNode();
+ }
+
+ return p;
+
+}
+
+AbstractFilesystemNode *GBAMPFileSystemNode::child(const Common::String& name) const {
+ if (_path.lastChar() == '\\') {
+ return new DSFileSystemNode(_path + name);
+ } else {
+ return new DSFileSystemNode(_path + "\\" + name);
+ }
+
+ return NULL;
+}
+
+bool GBAMPFileSystemNode::listDir(AbstractFSList& dirList, ListMode mode) const {
+// consolePrintf("Listdir\n");
+
+ enum { TYPE_NO_MORE = 0, TYPE_FILE = 1, TYPE_DIR = 2 };
+
+ char temp[128], fname[128], *path, *pathTemp;
+ strcpy(temp, _path.c_str());
+
+ path = temp + 3;
+
+ pathTemp = path;
+ while (*pathTemp) {
+ if (*pathTemp == '\\') {
+ *pathTemp = '/';
+ }
+ pathTemp++;
+ }
+
+
+// consolePrintf("This dir: %s\n", path);
+ FAT_chdir(path);
+
+ int entryType = FAT_FindFirstFile(fname);
+
+ while (entryType != TYPE_NO_MORE) {
+
+ if ( ((entryType == TYPE_DIR) && ((mode == FilesystemNode::kListDirectoriesOnly) || (mode == FilesystemNode::kListAll)))
+ || ((entryType == TYPE_FILE) && ((mode == FilesystemNode::kListFilesOnly) || (mode == FilesystemNode::kListAll))) ) {
+ GBAMPFileSystemNode* dsfsn;
+
+ if (strcmp(fname, ".") && strcmp(fname, "..")) {
+
+ if (!strcmp(path, "/")) {
+ dsfsn = new GBAMPFileSystemNode("mp:" + String(path) + String(fname), entryType == TYPE_DIR);
+ } else {
+ dsfsn = new GBAMPFileSystemNode("mp:" + String(path) + String("/") + String(fname), entryType == TYPE_DIR);
+ }
+
+// dsfsn->_isDirectory = entryType == DIR;
+ dirList.push_back((dsfsn));
+ }
+
+
+ } else {
+// consolePrintf("Skipping %s\n", fname);
+ }
+
+ entryType = FAT_FindNextFile(fname);
+ }
+
+// consolePrintf("No more");
+
+ FAT_chdir("/");
+
+ return true;
+}
+
+
+// Stdio replacements
+#define MAX_FILE_HANDLES 32
+
+bool inited = false;
+DS::fileHandle handle[MAX_FILE_HANDLES];
+
+FILE* std_fopen(const char* name, const char* mode) {
+
+
+
+ if (!inited) {
+ for (int r = 0; r < MAX_FILE_HANDLES; r++) {
+ handle[r].used = false;
+ }
+ inited = true;
+ currentDir[0] = '\0';
+ }
+
+
+
+
+ char* realName = (char *) name;
+
+ // Remove file system prefix
+ if ((name[0] == 'd') && (name[1] == 's') && (name[2] == ':') && (name[3] == '/')) {
+ realName += 4;
+ }
+
+ if ((name[0] == 'm') && (name[1] == 'p') && (name[2] == ':') && (name[3] == '/')) {
+ realName += 4;
+ }
+
+// consolePrintf("Open file:");
+// consolePrintf("'%s', [%s]", realName, mode);
+
+
+ if (DS::isGBAMPAvailable()) {
+ FAT_chdir("/");
+
+ char* p = realName;
+ while (*p) {
+ if (*p == '\\') *p = '/';
+ p++;
+ }
+
+ FAT_FILE* result = FAT_fopen(realName, mode);
+
+ if (result == 0) {
+// consolePrintf("Error code %d\n", result);
+ //consolePrintf("Opening file %s\n", realName);
+ } else {
+// consolePrintf("Opened file %d\n", result);
+ }
+// MT_memoryReport();
+
+ return (fileHandle *) result;
+ }
+
+
+ // Fail to open file for writing. It's in ROM!
+
+ // Allocate a file handle
+ int r = 0;
+ while (handle[r].used) r++;
+
+ if (strchr(mode, 'w')) {
+// consolePrintf("Writing %s\n", realName);
+ handle[r].sramFile = (DSSaveFile *) DSSaveFileManager::instance()->openSavefile(realName, true);
+ } else {
+// consolePrintf("Reading %s\n", realName);
+ handle[r].sramFile = (DSSaveFile *) DSSaveFileManager::instance()->openSavefile(realName, false);
+ }
+
+
+ if (handle[r].sramFile) {
+ handle[r].used = true;
+ handle[r].pos = 0;
+ handle[r].data = NULL;
+ handle[r].size = handle[r].sramFile->getSize();
+// consolePrintf("Found it");
+ return &handle[r];
+ }
+
+// consolePrintf("Not in SRAM!");
+
+ char* data;
+
+ ZipFile* zip = DSFileSystemNode::getZip();
+ if (!zip) {
+// consolePrintf("No zip yet!");
+ return NULL;
+ }
+
+ // Grab the data if it exists
+
+ zip->setAllFilesVisible(true);
+
+ if (currentDir[0] != 0) {
+ char nameWithPath[128];
+ sprintf(nameWithPath, "%s\%s", currentDir, realName);
+ strcpy(realName, nameWithPath);
+ }
+
+// consolePrintf("fopen(%s, %s)\n", realName, name);
+
+ if (zip->findFile(realName)) {
+ data = zip->getFile();
+ zip->setAllFilesVisible(false);
+
+ // Allocate a file handle
+ int r = 0;
+ while (handle[r].used) r++;
+
+
+ handle[r].used = true;
+ handle[r].pos = 0;
+ handle[r].data = data;
+ handle[r].size = zip->getFileSize();
+
+// consolePrintf("Opened file %d: %s (%s) ", r, realName, name);
+ return &handle[r];
+ } else {
+ zip->setAllFilesVisible(false);
+// consolePrintf("Not found: %s (%s) ", realName, name);
+ return NULL;
+ }
+}
+void std_fclose(FILE* handle) {
+
+ if (DS::isGBAMPAvailable()) {
+ FAT_fclose((FAT_FILE *) handle);
+ return;
+ }
+
+ handle->used = false;
+ if (handle->sramFile) {
+ delete handle->sramFile;
+ handle->sramFile = NULL;
+ }
+}
+
+size_t std_fread(const void* ptr, size_t size, size_t numItems, FILE* handle) {
+
+// consolePrintf("fread %d,%d %d ", size, numItems, ptr);
+
+
+
+ if (DS::isGBAMPAvailable()) {
+
+
+ int bytes = FAT_fread((void *) ptr, size, numItems, (FAT_FILE *) handle);
+ if (!std_feof(handle)) {
+ return numItems;
+ } else {
+// consolePrintf("Read past end of file: %d read out of %d\n", bytes / size, numItems);
+ return bytes / size;
+ }
+ return numItems;
+
+/* int item = 0;
+ u8* data = (u8 *) ptr;
+ while ((item < numItems) && (!FAT_feof((FAT_FILE *) handle))) {
+
+
+ int bytes = 0;
+ while ((bytes < size) && (!FAT_feof((FAT_FILE *) handle))) {
+ *data++ = FAT_fgetc((FAT_FILE *) handle);
+ bytes++;
+ }
+
+ item++;
+
+ }
+
+ return item;*/
+
+
+ int items = 0;
+
+ //for (int r = 0; r < numItems; r++) {
+ if (!std_feof(handle)) {
+
+
+
+/* for (int t = 0; t < size; t++) {
+ if (feof(handle)) eof = true;
+ *(((char *) (ptr)) + r * size + t) = getc(handle);
+ }*/
+ int left = size * numItems;;
+ int bytesRead = -1;
+ while ((left > 0) && (!FAT_feof((FAT_FILE *) handle))) {
+ int amount = left > 8192? 8192: left;
+// do {
+ bytesRead = FAT_fread((void *) ptr, 1, amount, (FAT_FILE *) handle);
+ /* if (bytesRead == 0) {
+ consolePrintf("Pos:%d items:%d num:%d amount:%d read:%d\n", ftell(handle), items, numItems, amount, bytesRead);
+ left++;
+
+ int pos = ftell(handle);
+
+ fseek(handle, 0, SEEK_SET);
+ int c = getc(handle);
+ fseek(handle, pos - 1024, SEEK_SET);
+ fread(ptr, 1024, 1, handle);
+ swiWaitForVBlank();
+ //while (true);
+ }*/
+ //} while (bytesRead == 0);
+ left -= bytesRead;
+ ptr = ((char *) (ptr)) + bytesRead;
+ }
+
+ items = numItems - (left / size);
+
+
+
+
+// FAT_fread((void *) ptr, size, 1, ((int) (handle)) - 1);
+ // ptr = ((char *) (ptr)) + size;
+
+ }
+ //}
+
+// consolePrintf("...done %d \n", items)
+
+ return items;
+
+ }
+
+ if (handle->sramFile) {
+ int bytes = 0;
+ int result = 1;
+ //consolePrintf("fread size=", size * numItems);
+ for (int r = 0; (r < (s32) size * (s32) numItems) && (result > 0); r++) {
+ result = handle->sramFile->read((void *) ( ((char *) (ptr)) + r), 1);
+ bytes += result;
+ //consolePrintf("'%d',", ((char *) (ptr))[0]);
+ }
+
+ handle->pos += bytes;
+
+ return bytes / size;
+ }
+
+
+ if (handle->pos + size * numItems > handle->size) {
+ numItems = (handle->size - handle->pos) / size;
+ if (numItems < 0) numItems = 0;
+ }
+
+// consolePrintf("read %d ", size * numItems);
+
+ memcpy((void *) ptr, handle->data + handle->pos, size * numItems);
+
+ handle->pos += size * numItems;
+
+
+ return numItems;
+}
+
+size_t std_fwrite(const void* ptr, size_t size, size_t numItems, FILE* handle) {
+ if ((handle == stdin)) return 0;
+
+ if ((handle == stderr) || (handle == stdout)) {
+// consolePrintf((char *) ptr);
+ return size;
+ }
+
+ //consolePrintf("fwrite size=%d\n", size * numItems);
+
+ if (DS::isGBAMPAvailable()) {
+
+ FAT_fwrite(((char *) (ptr)), size, numItems, (FAT_FILE *) handle);
+ return numItems;
+
+ int length = size * numItems;
+ int pos = 0;
+
+ while (pos < length) {
+ int amount = length > 512? 512: length;
+
+ FAT_fwrite(((char *) (ptr)) + pos, 1, amount, (FAT_FILE *) handle);
+ length -= amount;
+ pos += amount;
+ }
+
+ return numItems;
+ }
+
+
+ if (handle->sramFile) {
+ handle->sramFile->write(ptr, size);
+ return size;
+ } else {
+ return 0;
+ }
+}
+
+void std_fprintf(FILE* handle, const char* fmt, ...) {
+ consolePrintf(fmt);
+}
+
+bool std_feof(FILE* handle) {
+// consolePrintf("feof ");
+
+ if (DS::isGBAMPAvailable()) {
+ return FAT_feof((FAT_FILE *) handle);
+ }
+
+ if (handle->sramFile) {
+ return handle->sramFile->eos();
+ }
+
+// consolePrintf("feof %s", handle->pos >= handle->size? "true": "false");
+ return handle->pos >= handle->size;
+}
+
+void std_fflush(FILE* handle) {
+// consolePrintf("fflush ");
+}
+
+char* std_fgets(char* str, int size, FILE* file) {
+// consolePrintf("fgets file=%d ", file);
+
+ if (DS::isGBAMPAvailable()) {
+
+ char* s = str;
+ while ((*s++ = std_getc(file)) >= 32) {
+// consolePrintf("%d ", *s);
+ }
+ *s = 0;
+
+// consolePrintf("Read:%s\n", str);
+
+ return str;
+ }
+
+
+ if (file->sramFile) {
+ file->pos--;
+ int p = -1;
+ do {
+ file->pos++;
+ p++;
+ file->sramFile->read((char *) &str[p], 1);
+// consolePrintf("%d,", str[p]);
+ } while ((str[p] >= 32) && (!feof(file)) && (p < size));
+ str[p + 1] = 0;
+ file->pos++;
+// consolePrintf("Read:%s\n", str);
+ return str;
+ }
+
+ return NULL;
+}
+
+long int std_ftell(FILE* handle) {
+
+ if (DS::isGBAMPAvailable()) {
+ return FAT_ftell((FAT_FILE *) handle);
+ }
+
+ return handle->pos;
+}
+
+int std_fseek(FILE* handle, long int offset, int whence) {
+// consolePrintf("fseek %d %d ", offset, whence);
+
+ if (DS::isGBAMPAvailable()) {
+ return FAT_fseek((FAT_FILE *) handle, offset, whence);
+ }
+
+
+ switch (whence) {
+ case SEEK_CUR: {
+ handle->pos += offset;
+ break;
+ }
+
+ case SEEK_SET: {
+ handle->pos = offset;
+ break;
+ }
+
+ case SEEK_END: {
+ handle->pos = handle->size + offset;
+ break;
+ }
+
+ default: {
+ handle->pos = offset;
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+void std_clearerr(FILE* handle) {
+// consolePrintf("clearerr ");
+}
+
+int std_getc(FILE* handle) {
+
+ if (DS::isGBAMPAvailable()) {
+ char c;
+ FAT_fread(&c, 1, 1, (FAT_FILE *) handle);
+
+ return c;
+ }
+
+// consolePrintf("fgetc ");
+ return 0; // Not supported yet
+}
+
+char* std_getcwd(char* dir, int dunno) {
+// consolePrintf("getcwd ");
+ dir[0] = '\0';
+ return dir; // Not supported yet
+}
+
+void std_cwd(char* dir) {
+ char buffer[128];
+ strcpy(buffer, dir);
+ char* realName = buffer;
+
+ if (DS::isGBAMPAvailable()) {
+ if ((strlen(dir) >= 4) && (dir[0] == 'm') && (dir[1] == 'p') && (dir[2] == ':') && (dir[3] == '/')) {
+ realName += 4;
+ }
+
+ // consolePrintf("Real cwd:%d\n", realName);
+
+ char* p = realName;
+ while (*p) {
+ if (*p == '\\') *p = '/';
+ p++;
+ }
+
+ // consolePrintf("Real cwd:%d\n", realName);
+ FAT_chdir(realName);
+ } else {
+ if ((strlen(dir) >= 4) && (dir[0] == 'd') && (dir[1] == 's') && (dir[2] == ':') && (dir[3] == '/')) {
+ realName += 4;
+ }
+
+ char* p = realName;
+ while (*p) {
+ if (*p == '\\') *p = '/';
+ p++;
+ }
+
+ strcpy(currentDir, realName);
+ if (*(currentDir + strlen(currentDir) - 1) == '/') {
+ *(currentDir + strlen(currentDir) - 1) = '\0';
+ }
+ consolePrintf("CWD: %s\n", currentDir);
+ }
+}
+
+int std_ferror(FILE* handle) {
+ return 0;
+}
+
+} // namespace DS
+
+// These functions are added to AbstractFileSystemNode and are therefore outside
+// the DS namespace.
+
+AbstractFilesystemNode *AbstractFilesystemNode::getCurrentDirectory() {
+// consolePrintf("New node");
+
+ if (DS::isGBAMPAvailable()) {
+ return new DS::GBAMPFileSystemNode();
+ } else {
+ return new DS::DSFileSystemNode();
+ }
+}
+
+AbstractFilesystemNode* AbstractFilesystemNode::getNodeForPath(const String& path) {
+ if (DS::isGBAMPAvailable()) {
+ return new DS::GBAMPFileSystemNode(path);
+ } else {
+ return new DS::DSFileSystemNode(path);
+ }
+}
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include "stdafx.h"
+#include "str.h"
+#include "fs.h"
+#include "common/util.h"
+#include <NDS/ARM9/console.h> //basic print funcionality
+#include "ds-fs.h"
+#include "dsmain.h"
+#include "gba_nds_fat.h"
+
+namespace DS {
+
+//////////////////////////////////////////////////////////////
+// DSFileSystemNode - Flash ROM file system using Zip files
+//////////////////////////////////////////////////////////////
+
+ZipFile* DSFileSystemNode::_zipFile = NULL;
+char currentDir[128];
+
+DSFileSystemNode::DSFileSystemNode() {
+ _displayName = "ds:/";
+ _path = "ds:/";
+ _isValid = true;
+ _isDirectory = true;
+ _path = "ds:/";
+
+/* if (!_archive) {
+ _archive = (GBFS_FILE *) find_first_gbfs_file(scummdata);
+ if (!_archive) consolePrintf("No GBFS archive found!\n");
+ }*/
+
+ if (!_zipFile) {
+ _zipFile = new ZipFile();
+ }
+}
+
+DSFileSystemNode::DSFileSystemNode(const String& path) {
+// consolePrintf("--%s ",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+
+
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if (path[r] == '\\') {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ _displayName = String(disp);
+ _path = path;
+// _isValid = true;
+// _isDirectory = false;
+
+
+
+ if (!strncmp(pathStr, "ds:/", 4)) {
+ pathStr += 4;
+ }
+
+
+ if (*pathStr == '\0') {
+ _isValid = true;
+ _isDirectory = true;
+ return;
+ }
+
+ _zipFile->setAllFilesVisible(true);
+ if (_zipFile->findFile(pathStr)) {
+ _isValid = true;
+ _isDirectory = _zipFile->isDirectory();
+ } else {
+ _isValid = false;
+ _isDirectory = false;
+ }
+ _zipFile->setAllFilesVisible(false);
+
+// consolePrintf("%s - Found: %d, Dir: %d\n", pathStr, _isValid, _isDirectory);
+}
+
+DSFileSystemNode::DSFileSystemNode(const String& path, bool isDir) {
+// consolePrintf("--%s ",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if (path[r] == '\\') {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ _displayName = String(disp);
+ _path = path;
+ _isValid = true;
+ _isDirectory = isDir;
+
+// consolePrintf("Found: %d, Dir: %d\n", _isValid, _isDirectory);
+}
+
+DSFileSystemNode::DSFileSystemNode(const DSFileSystemNode* node) {
+
+}
+
+AbstractFilesystemNode* DSFileSystemNode::parent() const {
+// consolePrintf("parent\n");
+ DSFileSystemNode *p;
+
+ if (_path != "ds:/") {
+ char *path = (char *) _path.c_str();
+ int lastSlash = 4;
+
+ for (int r = 4; r < (int) strlen((char *) path); r++) {
+ if (path[r] == '\\') {
+ lastSlash = r;
+ }
+ }
+
+ p = new DSFileSystemNode(String(path, lastSlash));
+ ((DSFileSystemNode *) (p))->_isDirectory = true;
+ } else {
+ p = new DSFileSystemNode();
+ }
+
+ return p;
+
+}
+
+
+AbstractFilesystemNode *DSFileSystemNode::child(const Common::String& name) const {
+ if (_path.lastChar() == '\\') {
+ return new DSFileSystemNode(_path + name);
+ } else {
+ return new DSFileSystemNode(_path + "\\" + name);
+ }
+
+ return NULL;
+}
+
+
+bool DSFileSystemNode::listDir(AbstractFSList &dirList, ListMode mode) const {
+// consolePrintf("Listdir\n");
+
+
+// consolePrintf("Directory\n");
+
+
+ char temp[128];
+ strcpy(temp, _path.c_str());
+
+// consolePrintf("This dir: %s\n", temp);
+
+ if ((temp[0] == 'd') && (temp[1] == 's') && (temp[2] == ':') && (temp[3] == '/')) {
+ if (strlen(temp) != 4) {
+ _zipFile->changeDirectory(&temp[4]);
+ } else {
+ _zipFile->changeToRoot();
+
+/* // This is the root dir, so add the RAM folder
+ DSFileSystemNode* dsfsn = new DSFileSystemNode("ds:/ram");
+ dsfsn->_isDirectory = true;
+ dirList->push_back(wrap(dsfsn));*/
+ }
+ } else {
+ _zipFile->changeDirectory(temp);
+ }
+
+
+
+ if (_zipFile->restartFile()) {
+ do {
+ char name[128];
+ _zipFile->getFileName(name);
+
+// consolePrintf("file: %s\n", name);
+ if ( (_zipFile->isDirectory() && ((mode == FilesystemNode::kListDirectoriesOnly) || (mode == FilesystemNode::kListAll)) )
+ || (!_zipFile->isDirectory() && ((mode == FilesystemNode::kListFilesOnly) || (mode == FilesystemNode::kListAll)) ) )
+ {
+ DSFileSystemNode* dsfsn = new DSFileSystemNode("ds:/" + String(name), _zipFile->isDirectory());
+ dsfsn->_isDirectory = _zipFile->isDirectory();
+ dirList.push_back((dsfsn));
+ }
+
+ } while (_zipFile->skipFile());
+ }
+
+ return true;
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// GBAMPFileSystemNode - File system using GBA Movie Player and CF card
+/////////////////////////////////////////////////////////////////////////
+
+GBAMPFileSystemNode::GBAMPFileSystemNode() {
+ _displayName = "mp:/";
+ _path = "mp:/";
+ _isValid = true;
+ _isDirectory = true;
+ _path = "mp:/";
+}
+
+GBAMPFileSystemNode::GBAMPFileSystemNode(const String& path) {
+// consolePrintf("'%s'",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if ((path[r] == '\\') || (path[r] == '/')) {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ char check[128];
+ int success;
+
+ memset(check, 0, 128);
+ if (strlen(pathStr) > 3) {
+ strcpy(check, pathStr + 3);
+ if (check[strlen(check) - 1] == '/') {
+ check[strlen(check) - 1] = 0;
+ }
+ success = FAT_FileExists(check);
+ } else {
+ success = FT_DIR;
+ }
+// consolePrintf("Path: %s (%d)\n", check, success);
+
+ _displayName = String(disp);
+ _path = path;
+ _isValid = success == FT_FILE;
+ _isDirectory = success == FT_DIR;
+}
+
+GBAMPFileSystemNode::GBAMPFileSystemNode(const String& path, bool isDirectory) {
+// consolePrintf("'%s'",path.c_str());
+
+ char disp[128];
+ char* pathStr = (char *) path.c_str();
+ int lastSlash = 3;
+ for (int r = 0; r < (int) strlen(pathStr) - 1; r++) {
+ if ((path[r] == '\\') || (path[r] == '/')) {
+ lastSlash = r;
+ }
+ }
+
+ strcpy(disp, pathStr + lastSlash + 1);
+
+ _displayName = String(disp);
+ _path = path;
+ _isValid = true;
+ _isDirectory = isDirectory;
+}
+
+
+GBAMPFileSystemNode::GBAMPFileSystemNode(const GBAMPFileSystemNode* node) {
+
+}
+
+
+AbstractFilesystemNode* GBAMPFileSystemNode::parent() const {
+// consolePrintf("parent\n");
+ GBAMPFileSystemNode *p;
+
+ if (_path != "mp:/") {
+ char *path = (char *) _path.c_str();
+ int lastSlash = 4;
+
+ for (int r = 4; r < (int) strlen((char *) path); r++) {
+ if (path[r] == '/') {
+ lastSlash = r;
+ }
+ }
+
+ p = new GBAMPFileSystemNode(String(path, lastSlash));
+ p->_isDirectory = true;
+ } else {
+ p = new GBAMPFileSystemNode();
+ }
+
+ return p;
+
+}
+
+AbstractFilesystemNode *GBAMPFileSystemNode::child(const Common::String& name) const {
+ if (_path.lastChar() == '\\') {
+ return new DSFileSystemNode(_path + name);
+ } else {
+ return new DSFileSystemNode(_path + "\\" + name);
+ }
+
+ return NULL;
+}
+
+bool GBAMPFileSystemNode::listDir(AbstractFSList& dirList, ListMode mode) const {
+// consolePrintf("Listdir\n");
+
+ enum { TYPE_NO_MORE = 0, TYPE_FILE = 1, TYPE_DIR = 2 };
+
+ char temp[128], fname[128], *path, *pathTemp;
+ strcpy(temp, _path.c_str());
+
+ path = temp + 3;
+
+ pathTemp = path;
+ while (*pathTemp) {
+ if (*pathTemp == '\\') {
+ *pathTemp = '/';
+ }
+ pathTemp++;
+ }
+
+
+// consolePrintf("This dir: %s\n", path);
+ FAT_chdir(path);
+
+ int entryType = FAT_FindFirstFile(fname);
+
+ while (entryType != TYPE_NO_MORE) {
+
+ if ( ((entryType == TYPE_DIR) && ((mode == FilesystemNode::kListDirectoriesOnly) || (mode == FilesystemNode::kListAll)))
+ || ((entryType == TYPE_FILE) && ((mode == FilesystemNode::kListFilesOnly) || (mode == FilesystemNode::kListAll))) ) {
+ GBAMPFileSystemNode* dsfsn;
+
+ if (strcmp(fname, ".") && strcmp(fname, "..")) {
+
+ if (!strcmp(path, "/")) {
+ dsfsn = new GBAMPFileSystemNode("mp:" + String(path) + String(fname), entryType == TYPE_DIR);
+ } else {
+ dsfsn = new GBAMPFileSystemNode("mp:" + String(path) + String("/") + String(fname), entryType == TYPE_DIR);
+ }
+
+// dsfsn->_isDirectory = entryType == DIR;
+ dirList.push_back((dsfsn));
+ }
+
+
+ } else {
+// consolePrintf("Skipping %s\n", fname);
+ }
+
+ entryType = FAT_FindNextFile(fname);
+ }
+
+// consolePrintf("No more");
+
+ FAT_chdir("/");
+
+ return true;
+}
+
+
+// Stdio replacements
+#define MAX_FILE_HANDLES 32
+
+bool inited = false;
+DS::fileHandle handle[MAX_FILE_HANDLES];
+
+FILE* std_fopen(const char* name, const char* mode) {
+
+
+
+ if (!inited) {
+ for (int r = 0; r < MAX_FILE_HANDLES; r++) {
+ handle[r].used = false;
+ }
+ inited = true;
+ currentDir[0] = '\0';
+ }
+
+
+
+
+ char* realName = (char *) name;
+
+ // Remove file system prefix
+ if ((name[0] == 'd') && (name[1] == 's') && (name[2] == ':') && (name[3] == '/')) {
+ realName += 4;
+ }
+
+ if ((name[0] == 'm') && (name[1] == 'p') && (name[2] == ':') && (name[3] == '/')) {
+ realName += 4;
+ }
+
+// consolePrintf("Open file:");
+// consolePrintf("'%s', [%s]", realName, mode);
+
+
+ if (DS::isGBAMPAvailable()) {
+ FAT_chdir("/");
+
+ char* p = realName;
+ while (*p) {
+ if (*p == '\\') *p = '/';
+ p++;
+ }
+
+ FAT_FILE* result = FAT_fopen(realName, mode);
+
+ if (result == 0) {
+// consolePrintf("Error code %d\n", result);
+ //consolePrintf("Opening file %s\n", realName);
+ } else {
+// consolePrintf("Opened file %d\n", result);
+ }
+// MT_memoryReport();
+
+ return (fileHandle *) result;
+ }
+
+
+ // Fail to open file for writing. It's in ROM!
+
+ // Allocate a file handle
+ int r = 0;
+ while (handle[r].used) r++;
+
+ if (strchr(mode, 'w')) {
+// consolePrintf("Writing %s\n", realName);
+ handle[r].sramFile = (DSSaveFile *) DSSaveFileManager::instance()->openSavefile(realName, true);
+ } else {
+// consolePrintf("Reading %s\n", realName);
+ handle[r].sramFile = (DSSaveFile *) DSSaveFileManager::instance()->openSavefile(realName, false);
+ }
+
+
+ if (handle[r].sramFile) {
+ handle[r].used = true;
+ handle[r].pos = 0;
+ handle[r].data = NULL;
+ handle[r].size = handle[r].sramFile->getSize();
+// consolePrintf("Found it");
+ return &handle[r];
+ }
+
+// consolePrintf("Not in SRAM!");
+
+ char* data;
+
+ ZipFile* zip = DSFileSystemNode::getZip();
+ if (!zip) {
+// consolePrintf("No zip yet!");
+ return NULL;
+ }
+
+ // Grab the data if it exists
+
+ zip->setAllFilesVisible(true);
+
+ if (currentDir[0] != 0) {
+ char nameWithPath[128];
+ sprintf(nameWithPath, "%s\%s", currentDir, realName);
+ strcpy(realName, nameWithPath);
+ }
+
+// consolePrintf("fopen(%s, %s)\n", realName, name);
+
+ if (zip->findFile(realName)) {
+ data = zip->getFile();
+ zip->setAllFilesVisible(false);
+
+ // Allocate a file handle
+ int r = 0;
+ while (handle[r].used) r++;
+
+
+ handle[r].used = true;
+ handle[r].pos = 0;
+ handle[r].data = data;
+ handle[r].size = zip->getFileSize();
+
+// consolePrintf("Opened file %d: %s (%s) ", r, realName, name);
+ return &handle[r];
+ } else {
+ zip->setAllFilesVisible(false);
+// consolePrintf("Not found: %s (%s) ", realName, name);
+ return NULL;
+ }
+}
+void std_fclose(FILE* handle) {
+
+ if (DS::isGBAMPAvailable()) {
+ FAT_fclose((FAT_FILE *) handle);
+ return;
+ }
+
+ handle->used = false;
+ if (handle->sramFile) {
+ delete handle->sramFile;
+ handle->sramFile = NULL;
+ }
+}
+
+size_t std_fread(const void* ptr, size_t size, size_t numItems, FILE* handle) {
+
+// consolePrintf("fread %d,%d %d ", size, numItems, ptr);
+
+
+
+ if (DS::isGBAMPAvailable()) {
+
+
+ int bytes = FAT_fread((void *) ptr, size, numItems, (FAT_FILE *) handle);
+ if (!std_feof(handle)) {
+ return numItems;
+ } else {
+// consolePrintf("Read past end of file: %d read out of %d\n", bytes / size, numItems);
+ return bytes / size;
+ }
+ return numItems;
+
+/* int item = 0;
+ u8* data = (u8 *) ptr;
+ while ((item < numItems) && (!FAT_feof((FAT_FILE *) handle))) {
+
+
+ int bytes = 0;
+ while ((bytes < size) && (!FAT_feof((FAT_FILE *) handle))) {
+ *data++ = FAT_fgetc((FAT_FILE *) handle);
+ bytes++;
+ }
+
+ item++;
+
+ }
+
+ return item;*/
+
+
+ int items = 0;
+
+ //for (int r = 0; r < numItems; r++) {
+ if (!std_feof(handle)) {
+
+
+
+/* for (int t = 0; t < size; t++) {
+ if (feof(handle)) eof = true;
+ *(((char *) (ptr)) + r * size + t) = getc(handle);
+ }*/
+ int left = size * numItems;;
+ int bytesRead = -1;
+ while ((left > 0) && (!FAT_feof((FAT_FILE *) handle))) {
+ int amount = left > 8192? 8192: left;
+// do {
+ bytesRead = FAT_fread((void *) ptr, 1, amount, (FAT_FILE *) handle);
+ /* if (bytesRead == 0) {
+ consolePrintf("Pos:%d items:%d num:%d amount:%d read:%d\n", ftell(handle), items, numItems, amount, bytesRead);
+ left++;
+
+ int pos = ftell(handle);
+
+ fseek(handle, 0, SEEK_SET);
+ int c = getc(handle);
+ fseek(handle, pos - 1024, SEEK_SET);
+ fread(ptr, 1024, 1, handle);
+ swiWaitForVBlank();
+ //while (true);
+ }*/
+ //} while (bytesRead == 0);
+ left -= bytesRead;
+ ptr = ((char *) (ptr)) + bytesRead;
+ }
+
+ items = numItems - (left / size);
+
+
+
+
+// FAT_fread((void *) ptr, size, 1, ((int) (handle)) - 1);
+ // ptr = ((char *) (ptr)) + size;
+
+ }
+ //}
+
+// consolePrintf("...done %d \n", items)
+
+ return items;
+
+ }
+
+ if (handle->sramFile) {
+ int bytes = 0;
+ int result = 1;
+ //consolePrintf("fread size=", size * numItems);
+ for (int r = 0; (r < (s32) size * (s32) numItems) && (result > 0); r++) {
+ result = handle->sramFile->read((void *) ( ((char *) (ptr)) + r), 1);
+ bytes += result;
+ //consolePrintf("'%d',", ((char *) (ptr))[0]);
+ }
+
+ handle->pos += bytes;
+
+ return bytes / size;
+ }
+
+
+ if (handle->pos + size * numItems > handle->size) {
+ numItems = (handle->size - handle->pos) / size;
+ if (numItems < 0) numItems = 0;
+ }
+
+// consolePrintf("read %d ", size * numItems);
+
+ memcpy((void *) ptr, handle->data + handle->pos, size * numItems);
+
+ handle->pos += size * numItems;
+
+
+ return numItems;
+}
+
+size_t std_fwrite(const void* ptr, size_t size, size_t numItems, FILE* handle) {
+ if ((handle == stdin)) return 0;
+
+ if ((handle == stderr) || (handle == stdout)) {
+// consolePrintf((char *) ptr);
+ return size;
+ }
+
+ //consolePrintf("fwrite size=%d\n", size * numItems);
+
+ if (DS::isGBAMPAvailable()) {
+
+ FAT_fwrite(((char *) (ptr)), size, numItems, (FAT_FILE *) handle);
+ return numItems;
+
+ int length = size * numItems;
+ int pos = 0;
+
+ while (pos < length) {
+ int amount = length > 512? 512: length;
+
+ FAT_fwrite(((char *) (ptr)) + pos, 1, amount, (FAT_FILE *) handle);
+ length -= amount;
+ pos += amount;
+ }
+
+ return numItems;
+ }
+
+
+ if (handle->sramFile) {
+ handle->sramFile->write(ptr, size);
+ return size;
+ } else {
+ return 0;
+ }
+}
+
+void std_fprintf(FILE* handle, const char* fmt, ...) {
+ consolePrintf(fmt);
+}
+
+bool std_feof(FILE* handle) {
+// consolePrintf("feof ");
+
+ if (DS::isGBAMPAvailable()) {
+ return FAT_feof((FAT_FILE *) handle);
+ }
+
+ if (handle->sramFile) {
+ return handle->sramFile->eos();
+ }
+
+// consolePrintf("feof %s", handle->pos >= handle->size? "true": "false");
+ return handle->pos >= handle->size;
+}
+
+void std_fflush(FILE* handle) {
+// consolePrintf("fflush ");
+}
+
+char* std_fgets(char* str, int size, FILE* file) {
+// consolePrintf("fgets file=%d ", file);
+
+ if (DS::isGBAMPAvailable()) {
+
+ char* s = str;
+ while ((*s++ = std_getc(file)) >= 32) {
+// consolePrintf("%d ", *s);
+ }
+ *s = 0;
+
+// consolePrintf("Read:%s\n", str);
+
+ return str;
+ }
+
+
+ if (file->sramFile) {
+ file->pos--;
+ int p = -1;
+ do {
+ file->pos++;
+ p++;
+ file->sramFile->read((char *) &str[p], 1);
+// consolePrintf("%d,", str[p]);
+ } while ((str[p] >= 32) && (!feof(file)) && (p < size));
+ str[p + 1] = 0;
+ file->pos++;
+// consolePrintf("Read:%s\n", str);
+ return str;
+ }
+
+ return NULL;
+}
+
+long int std_ftell(FILE* handle) {
+
+ if (DS::isGBAMPAvailable()) {
+ return FAT_ftell((FAT_FILE *) handle);
+ }
+
+ return handle->pos;
+}
+
+int std_fseek(FILE* handle, long int offset, int whence) {
+// consolePrintf("fseek %d %d ", offset, whence);
+
+ if (DS::isGBAMPAvailable()) {
+ return FAT_fseek((FAT_FILE *) handle, offset, whence);
+ }
+
+
+ switch (whence) {
+ case SEEK_CUR: {
+ handle->pos += offset;
+ break;
+ }
+
+ case SEEK_SET: {
+ handle->pos = offset;
+ break;
+ }
+
+ case SEEK_END: {
+ handle->pos = handle->size + offset;
+ break;
+ }
+
+ default: {
+ handle->pos = offset;
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+void std_clearerr(FILE* handle) {
+// consolePrintf("clearerr ");
+}
+
+int std_getc(FILE* handle) {
+
+ if (DS::isGBAMPAvailable()) {
+ char c;
+ FAT_fread(&c, 1, 1, (FAT_FILE *) handle);
+
+ return c;
+ }
+
+// consolePrintf("fgetc ");
+ return 0; // Not supported yet
+}
+
+char* std_getcwd(char* dir, int dunno) {
+// consolePrintf("getcwd ");
+ dir[0] = '\0';
+ return dir; // Not supported yet
+}
+
+void std_cwd(char* dir) {
+ char buffer[128];
+ strcpy(buffer, dir);
+ char* realName = buffer;
+
+ if (DS::isGBAMPAvailable()) {
+ if ((strlen(dir) >= 4) && (dir[0] == 'm') && (dir[1] == 'p') && (dir[2] == ':') && (dir[3] == '/')) {
+ realName += 4;
+ }
+
+ // consolePrintf("Real cwd:%d\n", realName);
+
+ char* p = realName;
+ while (*p) {
+ if (*p == '\\') *p = '/';
+ p++;
+ }
+
+ // consolePrintf("Real cwd:%d\n", realName);
+ FAT_chdir(realName);
+ } else {
+ if ((strlen(dir) >= 4) && (dir[0] == 'd') && (dir[1] == 's') && (dir[2] == ':') && (dir[3] == '/')) {
+ realName += 4;
+ }
+
+ char* p = realName;
+ while (*p) {
+ if (*p == '\\') *p = '/';
+ p++;
+ }
+
+ strcpy(currentDir, realName);
+ if (*(currentDir + strlen(currentDir) - 1) == '/') {
+ *(currentDir + strlen(currentDir) - 1) = '\0';
+ }
+ consolePrintf("CWD: %s\n", currentDir);
+ }
+}
+
+int std_ferror(FILE* handle) {
+ return 0;
+}
+
+} // namespace DS
+
+// These functions are added to AbstractFileSystemNode and are therefore outside
+// the DS namespace.
+
+AbstractFilesystemNode *AbstractFilesystemNode::getCurrentDirectory() {
+// consolePrintf("New node");
+
+ if (DS::isGBAMPAvailable()) {
+ return new DS::GBAMPFileSystemNode();
+ } else {
+ return new DS::DSFileSystemNode();
+ }
+}
+
+AbstractFilesystemNode* AbstractFilesystemNode::getNodeForPath(const String& path) {
+ if (DS::isGBAMPAvailable()) {
+ return new DS::GBAMPFileSystemNode(path);
+ } else {
+ return new DS::DSFileSystemNode(path);
+ }
+} \ No newline at end of file
diff --git a/backends/fs/ds/ds-fs.h b/backends/fs/ds/ds-fs.h
new file mode 100644
index 0000000000..9b5f1fbad0
--- /dev/null
+++ b/backends/fs/ds/ds-fs.h
@@ -0,0 +1,356 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DS_FS_H
+#define _DS_FS_H
+
+
+#include "stdafx.h"
+#include "common/array.h"
+#include "common/str.h"
+
+//#include <NDS/ARM9/console.h>
+#include "fs.h"
+#include "zipreader.h"
+#include "ramsave.h"
+#include "scummconsole.h"
+#include "gba_nds_fat.h"
+#include "backends/fs/abstract-fs.h"
+//#include "backends/fs/fs.h"
+
+// Fix name clash with FOTAQ engine, cutaway.h
+#undef MAX_FILENAME_LENGTH
+// This class is used when a Flash cart is in use
+
+namespace DS {
+
+class DSFileSystemNode : public AbstractFilesystemNode {
+protected:
+ static ZipFile* _zipFile;
+
+ typedef class Common::String String;
+
+ String _displayName;
+ bool _isDirectory;
+ bool _isValid;
+ String _path;
+ int _refCountVal;
+
+public:
+ DSFileSystemNode();
+ DSFileSystemNode(const String &path);
+ DSFileSystemNode(const DSFileSystemNode *node);
+ DSFileSystemNode(const String& path, bool isDir);
+
+ virtual String displayName() const { return _displayName; }
+ virtual bool isValid() const { return _isValid; }
+ virtual bool isDirectory() const { return _isDirectory; }
+ virtual String path() const { return _path; }
+
+ virtual bool listDir(AbstractFSList &list, ListMode mode = FilesystemNode::kListDirectoriesOnly) const;
+ virtual AbstractFilesystemNode *parent() const;
+ virtual AbstractFilesystemNode *clone() const { return new DSFileSystemNode(this); }
+ virtual AbstractFilesystemNode *child(const Common::String& name) const;
+ static ZipFile* getZip() { return _zipFile; }
+};
+
+
+// This class is used when the GBAMP (GBA Movie Player) is used with a CompactFlash card
+
+class GBAMPFileSystemNode : public AbstractFilesystemNode {
+protected:
+ typedef class Common::String String;
+
+ String _displayName;
+ bool _isDirectory;
+ bool _isValid;
+ String _path;
+
+ int _refCountVal;
+
+public:
+ GBAMPFileSystemNode();
+ GBAMPFileSystemNode(const String &path);
+ GBAMPFileSystemNode(const String &path, bool isDirectory);
+ GBAMPFileSystemNode(const GBAMPFileSystemNode *node);
+
+ virtual String displayName() const { return _displayName; }
+ virtual bool isValid() const { return _isValid; }
+ virtual bool isDirectory() const { return _isDirectory; }
+ virtual String path() const { return _path; }
+ virtual bool listDir(AbstractFSList &list, ListMode mode = FilesystemNode::kListDirectoriesOnly) const;
+ virtual AbstractFilesystemNode *parent() const;
+ virtual AbstractFilesystemNode *clone() const { return new GBAMPFileSystemNode(this); }
+ virtual AbstractFilesystemNode *child(const Common::String& name) const;
+
+};
+
+
+// File reading
+
+
+struct fileHandle {
+ int pos;
+ bool used;
+ char* data;
+ int size;
+
+ DSSaveFile* sramFile;
+};
+
+// These functions replease the standard library functions of the same name.
+// As this header is included after the standard one, I have the chance to #define
+// all of these to my own code.
+//
+// A #define is the only way, as redefinig the functions would cause linker errors.
+
+// These functions need to be #undef'ed, as their definition is done with #includes
+#undef feof
+#undef stderr
+#undef stdout
+#undef stdin
+#undef clearerr
+#undef getc
+#undef ferror
+
+#define stdout ((DS::fileHandle*) -1)
+#define stderr ((DS::fileHandle*) -2)
+#define stdin ((DS::fileHandle*) -3)
+
+#define FILE DS::fileHandle
+//#define size_t int
+
+//#define FAT_chdir FAT_CWD
+
+FILE* std_fopen(const char* name, const char* mode);
+void std_fclose(FILE* handle);
+size_t std_fread(const void* ptr, size_t size, size_t numItems, FILE* handle);
+size_t std_fwrite(const void* ptr, size_t size, size_t numItems, FILE* handle);
+void std_fprintf(FILE* handle, const char* fmt, ...);
+bool std_feof(FILE* handle);
+void std_fflush(FILE* handle);
+char* std_fgets(char* str, int size, FILE* file);
+long int std_ftell(FILE* handle);
+int std_fseek(FILE* handle, long int offset, int whence);
+void std_clearerr(FILE* handle);
+int std_getc(FILE* handle);
+char* std_getcwd(char* dir, int dunno);
+void std_cwd(char* dir);
+int std_ferror(FILE* handle);
+
+// Only functions used in the ScummVM source have been defined here!
+#define fopen(name, mode) DS::std_fopen(name, mode)
+#define fclose(handle) DS::std_fclose(handle)
+#define fread(ptr, size, items, file) DS::std_fread(ptr, size, items, file)
+#define fwrite(ptr, size, items, file) DS::std_fwrite(ptr, size, items, file)
+#define feof(handle) DS::std_feof(handle)
+//#define fprintf(file, fmt, ...) DS::fprintf(file, fmt, ##__VA_ARGS__)
+#define fprintf(file, fmt, ...) { char str[128]; sprintf(str, fmt, ##__VA_ARGS__); DS::std_fwrite(str, strlen(str), 1, file); }
+#define printf(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
+#define fflush(file) DS::std_fflush(file)
+#define fgets(str, size, file) DS::std_fgets(str, size, file)
+#define ftell(handle) DS::std_ftell(handle)
+#define fseek(handle, offset, whence) DS::std_fseek(handle, offset, whence)
+#define clearerr(handle) DS::std_clearerr(handle)
+#define getc(handle) DS::std_getc(handle)
+#define getcwd(dir, dunno) DS::std_getcwd(dir, dunno)
+#define ferror(handle) DS::std_ferror(handle)
+
+
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DS_FS_H
+#define _DS_FS_H
+
+
+#include "stdafx.h"
+#include "common/array.h"
+#include "common/str.h"
+
+//#include <NDS/ARM9/console.h>
+#include "fs.h"
+#include "zipreader.h"
+#include "ramsave.h"
+#include "scummconsole.h"
+#include "gba_nds_fat.h"
+#include "backends/fs/abstract-fs.h"
+//#include "backends/fs/fs.h"
+
+// Fix name clash with FOTAQ engine, cutaway.h
+#undef MAX_FILENAME_LENGTH
+// This class is used when a Flash cart is in use
+
+namespace DS {
+
+class DSFileSystemNode : public AbstractFilesystemNode {
+protected:
+ static ZipFile* _zipFile;
+
+ typedef class Common::String String;
+
+ String _displayName;
+ bool _isDirectory;
+ bool _isValid;
+ String _path;
+ int _refCountVal;
+
+public:
+ DSFileSystemNode();
+ DSFileSystemNode(const String &path);
+ DSFileSystemNode(const DSFileSystemNode *node);
+ DSFileSystemNode(const String& path, bool isDir);
+
+ virtual String displayName() const { return _displayName; }
+ virtual bool isValid() const { return _isValid; }
+ virtual bool isDirectory() const { return _isDirectory; }
+ virtual String path() const { return _path; }
+
+ virtual bool listDir(AbstractFSList &list, ListMode mode = FilesystemNode::kListDirectoriesOnly) const;
+ virtual AbstractFilesystemNode *parent() const;
+ virtual AbstractFilesystemNode *clone() const { return new DSFileSystemNode(this); }
+ virtual AbstractFilesystemNode *child(const Common::String& name) const;
+ static ZipFile* getZip() { return _zipFile; }
+};
+
+
+// This class is used when the GBAMP (GBA Movie Player) is used with a CompactFlash card
+
+class GBAMPFileSystemNode : public AbstractFilesystemNode {
+protected:
+ typedef class Common::String String;
+
+ String _displayName;
+ bool _isDirectory;
+ bool _isValid;
+ String _path;
+
+ int _refCountVal;
+
+public:
+ GBAMPFileSystemNode();
+ GBAMPFileSystemNode(const String &path);
+ GBAMPFileSystemNode(const String &path, bool isDirectory);
+ GBAMPFileSystemNode(const GBAMPFileSystemNode *node);
+
+ virtual String displayName() const { return _displayName; }
+ virtual bool isValid() const { return _isValid; }
+ virtual bool isDirectory() const { return _isDirectory; }
+ virtual String path() const { return _path; }
+ virtual bool listDir(AbstractFSList &list, ListMode mode = FilesystemNode::kListDirectoriesOnly) const;
+ virtual AbstractFilesystemNode *parent() const;
+ virtual AbstractFilesystemNode *clone() const { return new GBAMPFileSystemNode(this); }
+ virtual AbstractFilesystemNode *child(const Common::String& name) const;
+
+};
+
+
+// File reading
+
+
+struct fileHandle {
+ int pos;
+ bool used;
+ char* data;
+ int size;
+
+ DSSaveFile* sramFile;
+};
+
+// These functions replease the standard library functions of the same name.
+// As this header is included after the standard one, I have the chance to #define
+// all of these to my own code.
+//
+// A #define is the only way, as redefinig the functions would cause linker errors.
+
+// These functions need to be #undef'ed, as their definition is done with #includes
+#undef feof
+#undef stderr
+#undef stdout
+#undef stdin
+#undef clearerr
+#undef getc
+#undef ferror
+
+#define stdout ((DS::fileHandle*) -1)
+#define stderr ((DS::fileHandle*) -2)
+#define stdin ((DS::fileHandle*) -3)
+
+#define FILE DS::fileHandle
+//#define size_t int
+
+//#define FAT_chdir FAT_CWD
+
+FILE* std_fopen(const char* name, const char* mode);
+void std_fclose(FILE* handle);
+size_t std_fread(const void* ptr, size_t size, size_t numItems, FILE* handle);
+size_t std_fwrite(const void* ptr, size_t size, size_t numItems, FILE* handle);
+void std_fprintf(FILE* handle, const char* fmt, ...);
+bool std_feof(FILE* handle);
+void std_fflush(FILE* handle);
+char* std_fgets(char* str, int size, FILE* file);
+long int std_ftell(FILE* handle);
+int std_fseek(FILE* handle, long int offset, int whence);
+void std_clearerr(FILE* handle);
+int std_getc(FILE* handle);
+char* std_getcwd(char* dir, int dunno);
+void std_cwd(char* dir);
+int std_ferror(FILE* handle);
+
+// Only functions used in the ScummVM source have been defined here!
+#define fopen(name, mode) DS::std_fopen(name, mode)
+#define fclose(handle) DS::std_fclose(handle)
+#define fread(ptr, size, items, file) DS::std_fread(ptr, size, items, file)
+#define fwrite(ptr, size, items, file) DS::std_fwrite(ptr, size, items, file)
+#define feof(handle) DS::std_feof(handle)
+//#define fprintf(file, fmt, ...) DS::fprintf(file, fmt, ##__VA_ARGS__)
+#define fprintf(file, fmt, ...) { char str[128]; sprintf(str, fmt, ##__VA_ARGS__); DS::std_fwrite(str, strlen(str), 1, file); }
+#define printf(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
+#define fflush(file) DS::std_fflush(file)
+#define fgets(str, size, file) DS::std_fgets(str, size, file)
+#define ftell(handle) DS::std_ftell(handle)
+#define fseek(handle, offset, whence) DS::std_fseek(handle, offset, whence)
+#define clearerr(handle) DS::std_clearerr(handle)
+#define getc(handle) DS::std_getc(handle)
+#define getcwd(dir, dunno) DS::std_getcwd(dir, dunno)
+#define ferror(handle) DS::std_ferror(handle)
+
+
+}
+
+#endif
diff --git a/backends/platform/ds/arm7/Makefile b/backends/platform/ds/arm7/Makefile
new file mode 100644
index 0000000000..349de22427
--- /dev/null
+++ b/backends/platform/ds/arm7/Makefile
@@ -0,0 +1,436 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------------
+# TARGET is the name of the output, if this ends with _mb generates a multiboot image
+# BUILD is the directory where object files & intermediate files will be placed
+# SOURCES is a list of directories containing source code
+# INCLUDES is a list of directories containing extra header files
+#---------------------------------------------------------------------------------
+TARGET := arm7
+BUILD := build
+SOURCES := gfx source data
+INCLUDES := include build
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -mthumb-interwork
+
+# note: arm9tdmi isn't the correct CPU arch, but anything newer and LD
+# *insists* it has a FPU or VFP, and it won't take no for an answer!
+CFLAGS := -g -Wall -O2\
+ -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
+ -ffast-math \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) -DARM7
+
+CXXFLAGS := $(CFLAGS) -fno-exceptions -fno-rtti
+ASFLAGS := -g $(ARCH)
+LDFLAGS := -g $(ARCH) -mno-fpu
+
+#---------------------------------------------------------------------------------
+# path to tools - this can be deleted if you set the path in windows
+#---------------------------------------------------------------------------------
+# export PATH := /d/dev/ds/devkitARM_r11/bin:/bin
+
+#---------------------------------------------------------------------------------
+# PATH to ndslib - just make a system variable called NDSLIBPATH and be done with it
+#---------------------------------------------------------------------------------
+# NDSLIBPATH := /d/dev/ds/ndslib/
+
+#---------------------------------------------------------------------------------
+# the prefix on the compiler executables
+#---------------------------------------------------------------------------------
+PREFIX := arm-eabi-
+#---------------------------------------------------------------------------------
+# any extra libraries we wish to link with the project
+#---------------------------------------------------------------------------------
+LIBS := -lnds7
+
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(DEVKITPRO)/libnds
+
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
+
+export CC := $(PREFIX)gcc
+export CXX := $(PREFIX)g++
+export AR := $(PREFIX)ar
+export OBJCOPY := $(PREFIX)objcopy
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+export LD := $(CXX)
+#export LD := $(CC)
+
+CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+PCXFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcx)))
+BINFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.bin)))
+PALFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pal)))
+RAWFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.raw)))
+MAPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.map)))
+
+export OFILES := $(MAPFILES:.map=.o) $(RAWFILES:.raw=.o) $(PALFILES:.pal=.o) $(BINFILES:.bin=.o) $(PCXFILES:.pcx=.o)\
+ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) -I- -I$(CURDIR)/../commoninclude\
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include/NDS)\
+ -I$(CURDIR)/$(BUILD)
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+.PHONY: $(BUILD) clean
+
+#---------------------------------------------------------------------------------
+$(BUILD):
+ @[ -d $@ ] || mkdir -p $@
+ @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...$(TARGET)
+ @rm -fr $(BUILD) *.bin
+
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+
+$(OUTPUT).bin : $(OUTPUT).elf
+
+$(OUTPUT).elf : $(OFILES)
+
+
+
+#---------------------------------------------------------------------------------
+%.bin: %.elf
+ @echo built ... $(notdir $@)
+ @$(OBJCOPY) -O binary $(TARGET).elf $@
+
+#---------------------------------------------------------------------------------
+%.elf:
+ echo ELF
+ @$(LD) $(LDFLAGS) -specs=ds_arm7.specs $(OFILES) $(LIBPATHS) $(LIBS) -o $(TARGET).elf
+
+
+
+#---------------------------------------------------------------------------------
+# Compile Targets for C/C++
+#---------------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------------
+%.o : %.cpp
+ @echo $(notdir $<)
+ @echo $(CXX) -MM $(CXXFLAGS) -o $*.d $<
+ @$(CXX) -MM $(CXXFLAGS) -o $*.d $<
+ @$(CXX) $(CXXFLAGS) -c $< -o$@
+
+#---------------------------------------------------------------------------------
+%.o : %.c
+ @echo $(notdir $<)
+ @$(CC) -MM $(CFLAGS) -o $*.d $<
+ @$(CC) $(CFLAGS) -c $< -o$@
+
+#---------------------------------------------------------------------------------
+%.o : %.s
+ @echo $(notdir $<)
+ @$(CC) -MM $(CFLAGS) -o $*.d $<
+ @$(CC) $(ASFLAGS) -c $< -o$@
+
+define bin2o
+ cp $(<) $(*).tmp
+ $(OBJCOPY) -I binary -O elf32-littlearm -B arm \
+ --rename-section .data=.rodata \
+ --redefine-sym _binary_$*_tmp_start=$*\
+ --redefine-sym _binary_$*_tmp_end=$*_end\
+ --redefine-sym _binary_$*_tmp_size=$*_size\
+ $(*).tmp $(@)
+ echo "extern const u8" $(*)"[];" > $(*).h
+ echo "extern const u32" $(*)_size[]";" >> $(*).h
+ rm $(*).tmp
+endef
+
+#---------------------------------------------------------------------------------
+%.o : %.pcx
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.raw
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.pal
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.map
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.mdl
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------------
+# TARGET is the name of the output, if this ends with _mb generates a multiboot image
+# BUILD is the directory where object files & intermediate files will be placed
+# SOURCES is a list of directories containing source code
+# INCLUDES is a list of directories containing extra header files
+#---------------------------------------------------------------------------------
+TARGET := arm7
+BUILD := build
+SOURCES := gfx source data
+INCLUDES := include build
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -mthumb-interwork
+
+# note: arm9tdmi isn't the correct CPU arch, but anything newer and LD
+# *insists* it has a FPU or VFP, and it won't take no for an answer!
+CFLAGS := -g -Wall -O2\
+ -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
+ -ffast-math \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) -DARM7
+
+CXXFLAGS := $(CFLAGS) -fno-exceptions -fno-rtti
+ASFLAGS := -g $(ARCH)
+LDFLAGS := -g $(ARCH) -mno-fpu
+
+#---------------------------------------------------------------------------------
+# path to tools - this can be deleted if you set the path in windows
+#---------------------------------------------------------------------------------
+# export PATH := /d/dev/ds/devkitARM_r11/bin:/bin
+
+#---------------------------------------------------------------------------------
+# PATH to ndslib - just make a system variable called NDSLIBPATH and be done with it
+#---------------------------------------------------------------------------------
+# NDSLIBPATH := /d/dev/ds/ndslib/
+
+#---------------------------------------------------------------------------------
+# the prefix on the compiler executables
+#---------------------------------------------------------------------------------
+PREFIX := arm-eabi-
+#---------------------------------------------------------------------------------
+# any extra libraries we wish to link with the project
+#---------------------------------------------------------------------------------
+LIBS := -lnds7
+
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(DEVKITPRO)/libnds
+
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
+
+export CC := $(PREFIX)gcc
+export CXX := $(PREFIX)g++
+export AR := $(PREFIX)ar
+export OBJCOPY := $(PREFIX)objcopy
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+export LD := $(CXX)
+#export LD := $(CC)
+
+CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+PCXFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcx)))
+BINFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.bin)))
+PALFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pal)))
+RAWFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.raw)))
+MAPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.map)))
+
+export OFILES := $(MAPFILES:.map=.o) $(RAWFILES:.raw=.o) $(PALFILES:.pal=.o) $(BINFILES:.bin=.o) $(PCXFILES:.pcx=.o)\
+ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) -I- -I$(CURDIR)/../commoninclude\
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include/NDS)\
+ -I$(CURDIR)/$(BUILD)
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+.PHONY: $(BUILD) clean
+
+#---------------------------------------------------------------------------------
+$(BUILD):
+ @[ -d $@ ] || mkdir -p $@
+ @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...$(TARGET)
+ @rm -fr $(BUILD) *.bin
+
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+
+$(OUTPUT).bin : $(OUTPUT).elf
+
+$(OUTPUT).elf : $(OFILES)
+
+
+
+#---------------------------------------------------------------------------------
+%.bin: %.elf
+ @echo built ... $(notdir $@)
+ @$(OBJCOPY) -O binary $(TARGET).elf $@
+
+#---------------------------------------------------------------------------------
+%.elf:
+ echo ELF
+ @$(LD) $(LDFLAGS) -specs=ds_arm7.specs $(OFILES) $(LIBPATHS) $(LIBS) -o $(TARGET).elf
+
+
+
+#---------------------------------------------------------------------------------
+# Compile Targets for C/C++
+#---------------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------------
+%.o : %.cpp
+ @echo $(notdir $<)
+ @echo $(CXX) -MM $(CXXFLAGS) -o $*.d $<
+ @$(CXX) -MM $(CXXFLAGS) -o $*.d $<
+ @$(CXX) $(CXXFLAGS) -c $< -o$@
+
+#---------------------------------------------------------------------------------
+%.o : %.c
+ @echo $(notdir $<)
+ @$(CC) -MM $(CFLAGS) -o $*.d $<
+ @$(CC) $(CFLAGS) -c $< -o$@
+
+#---------------------------------------------------------------------------------
+%.o : %.s
+ @echo $(notdir $<)
+ @$(CC) -MM $(CFLAGS) -o $*.d $<
+ @$(CC) $(ASFLAGS) -c $< -o$@
+
+define bin2o
+ cp $(<) $(*).tmp
+ $(OBJCOPY) -I binary -O elf32-littlearm -B arm \
+ --rename-section .data=.rodata \
+ --redefine-sym _binary_$*_tmp_start=$*\
+ --redefine-sym _binary_$*_tmp_end=$*_end\
+ --redefine-sym _binary_$*_tmp_size=$*_size\
+ $(*).tmp $(@)
+ echo "extern const u8" $(*)"[];" > $(*).h
+ echo "extern const u32" $(*)_size[]";" >> $(*).h
+ rm $(*).tmp
+endef
+
+#---------------------------------------------------------------------------------
+%.o : %.pcx
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.raw
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.pal
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.map
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.mdl
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#--------------------------------------------------------------------------------------- \ No newline at end of file
diff --git a/backends/platform/ds/arm7/source/main.cpp b/backends/platform/ds/arm7/source/main.cpp
new file mode 100644
index 0000000000..f5518be647
--- /dev/null
+++ b/backends/platform/ds/arm7/source/main.cpp
@@ -0,0 +1,1130 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header: /cvsroot/scummvm/scummvm/backends/fs/fs.cpp,v 1.3.2.1 2004/12/18 02:33:52 fingolfin Exp $
+ */
+
+//////////////////////////////////////////////////////////////////////
+// Simple ARM7 stub (sends RTC, TSC, and X/Y data to the ARM 9)
+// -- joat
+// -- modified by Darkain and others
+//////////////////////////////////////////////////////////////////////
+
+
+#include <nds.h>
+
+#include <bios.h>
+#include <arm7/touch.h>
+#include <arm7/clock.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nds/scummvm_ipc.h>
+//////////////////////////////////////////////////////////////////////
+
+
+#define TOUCH_CAL_X1 (*(vs16*)0x027FFCD8)
+#define TOUCH_CAL_Y1 (*(vs16*)0x027FFCDA)
+#define TOUCH_CAL_X2 (*(vs16*)0x027FFCDE)
+#define TOUCH_CAL_Y2 (*(vs16*)0x027FFCE0)
+#define SCREEN_WIDTH 256
+#define SCREEN_HEIGHT 192
+s32 TOUCH_WIDTH = TOUCH_CAL_X2 - TOUCH_CAL_X1;
+s32 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1;
+s32 TOUCH_OFFSET_X = ( ((SCREEN_WIDTH -60) * TOUCH_CAL_X1) / TOUCH_WIDTH ) - 28;
+s32 TOUCH_OFFSET_Y = ( ((SCREEN_HEIGHT-60) * TOUCH_CAL_Y1) / TOUCH_HEIGHT ) - 28;
+
+vu8* soundData;
+
+vu8* soundBuffer;
+vu8* arm9Buffer;
+bool soundFilled[4];
+
+int playingSection;
+
+bool needSleep = false;
+int temp;
+
+int adpcmBufferNum = 0;
+
+// those are pixel positions of the two points you click when calibrating
+#define TOUCH_CNTRL_X1 (*(vu8*)0x027FFCDC)
+#define TOUCH_CNTRL_Y1 (*(vu8*)0x027FFCDD)
+#define TOUCH_CNTRL_X2 (*(vu8*)0x027FFCE2)
+#define TOUCH_CNTRL_Y2 (*(vu8*)0x027FFCE3)
+
+
+//////////////////////////////////////////////////////////////////////
+
+/*
+void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F, u8 pan=63, u8 format=0) {
+ SCHANNEL_TIMER(channel) = SOUND_FREQ(sampleRate);
+ SCHANNEL_SOURCE(channel) = (uint32)data;
+ SCHANNEL_LENGTH(channel) = bytes;
+ SCHANNEL_CR(channel) = SOUND_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | (format==1?SOUND_8BIT:SOUND_16BIT);
+}
+
+
+s8 getFreeSoundChannel() {
+ for (int i=0; i<16; i++) {
+ if ( (SCHANNEL_CR(i) & SOUND_ENABLE) == 0 ) return i;
+ }
+ return -1;
+}
+*/
+
+
+s8 getFreeSoundChannel() {
+// return 0;
+ for (int i=0; i<16; i++) {
+ if ( (SCHANNEL_CR(i) & SCHANNEL_ENABLE) == 0 ) return i;
+ }
+ return -1;
+}
+
+void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F, u8 pan=63, u8 format=0) {
+ REG_IME = IME_DISABLE;
+
+ channel = getFreeSoundChannel();
+/* if (format == 2) {
+ channel = 1;
+ } else {
+ channel = 0;
+ }*/
+
+ if (channel > 1) channel = 1;
+
+ bytes &= ~7; // Multiple of 4 bytes!
+// bytes += 4;
+
+ SCHANNEL_CR(channel) = 0;
+ SCHANNEL_TIMER(channel) = SOUND_FREQ(sampleRate);
+ SCHANNEL_SOURCE(channel) = ((uint32) (data));
+ SCHANNEL_LENGTH(channel) = ((bytes & 0x7FFFFFFF) >> 2);
+ SCHANNEL_REPEAT_POINT(channel) = 0;
+
+ SCHANNEL_CR(channel + 2) = 0;
+ SCHANNEL_TIMER(channel + 2) = SOUND_FREQ(sampleRate);
+ SCHANNEL_SOURCE(channel + 2) = ((uint32) (data));
+ SCHANNEL_LENGTH(channel + 2) = ((bytes & 0x7FFFFFFF) >> 2);
+ SCHANNEL_REPEAT_POINT(channel + 2) = 0;
+
+ uint32 flags = SCHANNEL_ENABLE | SOUND_VOL(vol) | SOUND_PAN(pan);
+
+ switch (format) {
+ case 1: {
+ flags |= SOUND_8BIT;
+ flags |= SOUND_REPEAT;// | (1 << 15);
+ break;
+ }
+
+ case 0: {
+ flags |= SOUND_16BIT;
+ flags |= SOUND_REPEAT;// | (1 << 15);
+ break;
+ }
+
+ case 2: {
+ flags |= SOUND_FORMAT_ADPCM;
+ flags |= SOUND_ONE_SHOT;// | (1 << 15);
+
+ SCHANNEL_SOURCE(channel) = (unsigned int) IPC->adpcm.buffer[0];
+ //bytes += 32;
+ SCHANNEL_LENGTH(channel) = (((bytes + 4) & 0x7FFFFFFF) >> 2);
+
+ SCHANNEL_CR(channel + 1) = 0;
+ SCHANNEL_SOURCE(channel + 1) = (unsigned int) IPC->adpcm.buffer[0];
+ SCHANNEL_LENGTH(channel + 1) = (((bytes + 4) & 0x7FFFFFFF) >> 2);
+ SCHANNEL_TIMER(channel + 1) = SOUND_FREQ(sampleRate);
+ SCHANNEL_REPEAT_POINT(channel + 1) = 0;
+ SCHANNEL_CR(channel + 1) = flags;
+ temp = bytes;
+ adpcmBufferNum = 0;
+ break;
+ }
+ }
+
+
+// if (bytes & 0x80000000) {
+// flags |= SOUND_REPEAT;
+// } else {
+// }
+
+
+
+
+ soundData = (vu8* ) data;
+
+ SCHANNEL_CR(channel) = flags;
+ SCHANNEL_CR(channel + 2) = flags;
+
+
+
+ if (channel == 0) {
+ for (volatile int i = 0; i < 16384 * 2; i++) {
+ // Delay loop - this makes everything stay in sync!
+ }
+
+ TIMER0_CR = 0;
+ TIMER0_DATA = SOUND_FREQ(sampleRate) * 2;
+ TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1;
+
+ TIMER1_CR = 0;
+ TIMER1_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3); // Trigger four times during the length of the buffer
+ TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+
+ playingSection = 0;
+ } else {
+ for (volatile int i = 0; i < 16384 * 2; i++) {
+ // Delay loop - this makes everything stay in sync!
+ }
+
+ TIMER2_CR = 0;
+ TIMER2_DATA = SOUND_FREQ(sampleRate) * 2;
+ TIMER2_CR = TIMER_ENABLE | TIMER_DIV_1;
+
+ TIMER3_CR = 0;
+ TIMER3_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3); // Trigger four times during the length of the buffer
+ TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+
+ for (int r = 0; r < 4; r++) {
+// IPC->streamFillNeeded[r] = true;
+ }
+
+ IPC->streamPlayingSection = 0;
+ }
+
+
+
+// IPC->fillSoundFirstHalf = true;
+// IPC->fillSoundSecondHalf = true;
+// soundFirstHalf = true;
+
+ REG_IME = IME_ENABLE;
+}
+
+void stopSound(int chan) {
+ SCHANNEL_CR(chan) = 0;
+}
+
+void DummyHandler() {
+ REG_IF = REG_IF;
+}
+
+uint16 powerManagerWrite(uint32 command, u32 data, bool enable) {
+
+ uint16 result;
+ SerialWaitBusy();
+
+ // Write the command and wait for it to complete
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11);
+ REG_SPIDATA = command | 0x80;
+ SerialWaitBusy();
+
+ // Write the second command and clock in the data
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz;
+ REG_SPIDATA = 0;
+ SerialWaitBusy();
+
+ result = REG_SPIDATA & 0xFF;
+
+
+
+ // Write the command and wait for it to complete
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11);
+ REG_SPIDATA = command;
+ SerialWaitBusy();
+
+ // Write the second command and clock in the data
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz;
+ REG_SPIDATA = enable? (result | data): (result & ~data);
+ SerialWaitBusy();
+}
+
+/*
+void performSleep() {
+
+ powerManagerWrite(0, 0x30, true);
+
+ // Here, I set up a dummy interrupt handler, then trigger all interrupts.
+ // These are just aknowledged by the handler without doing anything else.
+ // Why? Because without it the sleep mode will only happen once, and then
+ // never again. I got the idea from reading the MoonShell source.
+ IME = 0;
+ u32 irq = (u32) IRQ_HANDLER;
+ IRQ_HANDLER = DummyHandler;
+ IF = ~0;
+ IME = 1;
+
+
+ // Now save which interrupts are enabled, then set only the screens unfolding
+ // interrupt to be enabled, so that the first interrupt that happens is the
+ // one I want.
+ int saveInts = IE;
+
+
+
+ IE = IRQ_TIMER0; // Screens unfolding interrupt
+
+ // Now call the sleep function in the bios
+ bool b;
+ do {
+ TIMER0_CR = 0;
+ TIMER0_DATA = TIMER_FREQ(20);
+ TIMER0_CR = TIMER_ENABLE | TIMER_DIV_64;
+
+ swiDelay(100);
+
+ swiSleep();
+
+ swiDelay(100);
+
+ powerManagerWrite(0, 0x30, b = !b);
+ } while (!(TIMER0_CR & TIMER_ENABLE));
+
+ TIMER0_CR = 0;
+
+ // We're back from sleep, now restore the interrupt state and IRQ handler
+ IRQ_HANDLER = (void (*)()) irq;
+ IE = saveInts;
+ IF = ~0;
+ IME = 1;
+
+
+
+ powerManagerWrite(0, 0x30, false);
+}
+*/
+
+
+void performSleep() {
+ powerManagerWrite(0, 0x30, true);
+
+ IPC->performArm9SleepMode = true; // Tell ARM9 to sleep
+
+// u32 irq = (u32) IRQ_HANDLER;
+// IRQ_HANDLER = DummyHandler;
+// POWER_CR &= ~POWER_SOUND;
+
+// int saveInts = REG_IE;
+// REG_IE = (1 << 22) | IRQ_VBLANK; // Lid open
+// *((u32*) (0x0380FFF8)) = *((u32*) (0x0380FFF8)) | (REG_IE & REG_IF);
+// VBLANK_INTR_WAIT_FLAGS = IRQ_VBLANK;
+
+
+ int r = 0;
+ while ((REG_KEYXY & (1 << 7))) { // Wait for lid to open
+ swiDelay(1000000);
+ r++;
+ }
+
+// IRQ_HANDLER = (void (*)()) irq;
+ IPC->performArm9SleepMode = false; // Tell ARM9 to wake up
+// REG_IE = saveInts;
+
+// POWER_CR |= POWER_SOUND;
+
+ powerManagerWrite(0, 0x30, false);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+
+void InterruptHandler(void) {
+ static int heartbeat = 0;
+
+ if (REG_IF & IRQ_TIMER1) {
+
+ IPC->fillNeeded[playingSection] = true;
+ soundFilled[playingSection] = false;
+
+ if (playingSection == 3) {
+// IME = IME_DISABLED;
+
+ // while (SCHANNEL_CR(0) & SCHANNEL_ENABLE) {
+ // }
+// SCHANNEL_CR(0) &= ~SCHANNEL_ENABLE;
+
+// SCHANNEL_CR(0) |= SCHANNEL_ENABLE;
+// TIMER1_CR = 0;
+// TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+
+ playingSection = 0;
+
+// IME = IME_ENABLED;
+ } else {
+ playingSection++;
+ }
+
+ IPC->playingSection = playingSection;
+
+/* for (int r = 0; r < 4; r++) {
+ //if ((!soundFilled[r]) && (!IPC->fillNeeded[playingSection])) {
+ memcpy((void *) (soundBuffer + (r * 1024)), (void *) (arm9Buffer + (r * 1024)), 1024);
+
+ vu16* p = (vu16 *) (soundBuffer);
+ //for (int t = 0; t < 2048; t++) {
+ // *(p + t) = (t & 1)? 0xF000: 0x0000;
+ //}
+ soundFilled[r] = true;
+ //}
+ }*/
+
+
+ REG_IF = IRQ_TIMER1;
+ }
+
+
+ if (REG_IF & IRQ_TIMER3) {
+ while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed
+ IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7
+
+ IPC->streamFillNeeded[IPC->streamPlayingSection] = true;
+
+ if (IPC->streamPlayingSection == 3) {
+ IPC->streamPlayingSection = 0;
+ } else {
+ IPC->streamPlayingSection++;
+ }
+
+ REG_IF = IRQ_TIMER3;
+
+ IPC->adpcm.semaphore = false;
+ }
+
+
+// IPC->performArm9SleepMode = false;
+
+ // precalculate some values
+// static int16 TOUCH_WIDTH = TOUCH_CAL_X2 - TOUCH_CAL_X1;
+// static int16 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1;
+// static int16 CNTRL_WIDTH = TOUCH_CNTRL_X2 - (TOUCH_CNTRL_X1 - 8);
+// static int16 CNTRL_HEIGHT = TOUCH_CNTRL_Y2 - (TOUCH_CNTRL_Y1 - 8);
+
+
+
+
+
+ if (REG_IF & IRQ_VBLANK) {
+ uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0, batt=0, aux=0;
+ int t1=0, t2=0;
+ uint32 temp=0;
+ uint8 ct[sizeof(IPC->curtime)];
+
+
+ // Update the heartbeat
+ heartbeat++;
+
+ // Read the X/Y buttons and the /PENIRQ line
+ but = REG_KEYXY;
+ if (!(but & 0x40)) {
+ // Read the touch screen
+ touchPosition p = touchReadXY();
+
+// x = touchRead(TSC_MEASURE_X);
+ // y = touchRead(TSC_MEASURE_Y);
+
+ x = p.x;
+ y = p.y;
+
+ xpx = p.px;
+ ypx = p.py;
+
+// xpx = ( ((SCREEN_WIDTH -60) * x) / TOUCH_WIDTH ) - TOUCH_OFFSET_X;
+ // ypx = ( ((SCREEN_HEIGHT-60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y;
+
+// xpx = (IPC->touchX - (int16) TOUCH_CAL_X1) * CNTRL_WIDTH / TOUCH_WIDTH + (int16) (TOUCH_CNTRL_X1 - 8);
+ // ypx = (IPC->touchY - (int16) TOUCH_CAL_Y1) * CNTRL_HEIGHT / TOUCH_HEIGHT + (int16) (TOUCH_CNTRL_Y1 - 8);
+
+
+ z1 = touchRead(TSC_MEASURE_Z1);
+ z2 = touchRead(TSC_MEASURE_Z2);
+ }
+
+ if (but & (1 << 7)) { // Check if screen is folded
+ needSleep = true;
+ }
+
+
+ batt = touchRead(TSC_MEASURE_BATTERY);
+ aux = touchRead(TSC_MEASURE_AUX);
+
+ // Read the time
+ rtcGetTime((uint8 *)ct);
+ BCDToInteger((uint8 *)&(ct[1]), 7);
+
+ // Read the temperature
+ temp = touchReadTemperature(&t1, &t2);
+
+ // Update the IPC struct
+ IPC->heartbeat = heartbeat;
+ IPC->buttons = but;
+ IPC->touchX = x;
+ IPC->touchY = y;
+ IPC->touchXpx = xpx;
+ IPC->touchYpx = ypx;
+ IPC->touchZ1 = z1;
+ IPC->touchZ2 = z2;
+ IPC->battery = batt;
+ IPC->aux = aux;
+
+ for(u32 i=0; i<sizeof(ct); i++) {
+ IPC->curtime[i] = ct[i];
+ }
+
+ IPC->temperature = temp;
+ IPC->tdiode1 = t1;
+ IPC->tdiode2 = t2;
+
+
+
+ //sound code :)
+ TransferSound *snd = IPC->soundData;
+ IPC->soundData = 0;
+ if (snd) {
+ for (int i=0; i<snd->count; i++) {
+ s8 chan = getFreeSoundChannel();
+ if (snd->data[i].rate > 0) {
+ if (chan >= 0) {
+ startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format);
+ }
+ } else {
+ stopSound(-snd->data[i].rate);
+ }
+ }
+ }
+
+ REG_IF = IRQ_VBLANK;
+ }
+
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+int main(int argc, char ** argv) {
+ // Reset the clock if needed
+ rtcReset();
+
+ //enable sound
+ powerON(POWER_SOUND);
+ SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F);
+ IPC->soundData = 0;
+ IPC->reset = false;
+
+
+ for (int r = 0; r < 8; r++) {
+ IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512);
+ }
+
+ for (int r = 0; r < 4; r++) {
+ soundFilled[r] = false;
+ }
+
+
+ // Set up the interrupt handler
+ REG_IME = 0;
+ IRQ_HANDLER = &InterruptHandler;
+ REG_IE = IRQ_VBLANK | IRQ_TIMER1 | IRQ_TIMER3;
+ REG_IF = ~0;
+ DISP_SR = DISP_VBLANK_IRQ;
+ REG_IME = 1;
+
+
+
+ // Keep the ARM7 out of main RAM
+ while (1) {
+ if (needSleep) {
+ performSleep();
+ needSleep = false;
+ }
+// if (IPC->reset) {
+// swiSoftReset();
+// }
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header: /cvsroot/scummvm/scummvm/backends/fs/fs.cpp,v 1.3.2.1 2004/12/18 02:33:52 fingolfin Exp $
+ */
+
+//////////////////////////////////////////////////////////////////////
+// Simple ARM7 stub (sends RTC, TSC, and X/Y data to the ARM 9)
+// -- joat
+// -- modified by Darkain and others
+//////////////////////////////////////////////////////////////////////
+
+
+#include <nds.h>
+
+#include <bios.h>
+#include <arm7/touch.h>
+#include <arm7/clock.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nds/scummvm_ipc.h>
+//////////////////////////////////////////////////////////////////////
+
+
+#define TOUCH_CAL_X1 (*(vs16*)0x027FFCD8)
+#define TOUCH_CAL_Y1 (*(vs16*)0x027FFCDA)
+#define TOUCH_CAL_X2 (*(vs16*)0x027FFCDE)
+#define TOUCH_CAL_Y2 (*(vs16*)0x027FFCE0)
+#define SCREEN_WIDTH 256
+#define SCREEN_HEIGHT 192
+s32 TOUCH_WIDTH = TOUCH_CAL_X2 - TOUCH_CAL_X1;
+s32 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1;
+s32 TOUCH_OFFSET_X = ( ((SCREEN_WIDTH -60) * TOUCH_CAL_X1) / TOUCH_WIDTH ) - 28;
+s32 TOUCH_OFFSET_Y = ( ((SCREEN_HEIGHT-60) * TOUCH_CAL_Y1) / TOUCH_HEIGHT ) - 28;
+
+vu8* soundData;
+
+vu8* soundBuffer;
+vu8* arm9Buffer;
+bool soundFilled[4];
+
+int playingSection;
+
+bool needSleep = false;
+int temp;
+
+int adpcmBufferNum = 0;
+
+// those are pixel positions of the two points you click when calibrating
+#define TOUCH_CNTRL_X1 (*(vu8*)0x027FFCDC)
+#define TOUCH_CNTRL_Y1 (*(vu8*)0x027FFCDD)
+#define TOUCH_CNTRL_X2 (*(vu8*)0x027FFCE2)
+#define TOUCH_CNTRL_Y2 (*(vu8*)0x027FFCE3)
+
+
+//////////////////////////////////////////////////////////////////////
+
+/*
+void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F, u8 pan=63, u8 format=0) {
+ SCHANNEL_TIMER(channel) = SOUND_FREQ(sampleRate);
+ SCHANNEL_SOURCE(channel) = (uint32)data;
+ SCHANNEL_LENGTH(channel) = bytes;
+ SCHANNEL_CR(channel) = SOUND_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | (format==1?SOUND_8BIT:SOUND_16BIT);
+}
+
+
+s8 getFreeSoundChannel() {
+ for (int i=0; i<16; i++) {
+ if ( (SCHANNEL_CR(i) & SOUND_ENABLE) == 0 ) return i;
+ }
+ return -1;
+}
+*/
+
+
+s8 getFreeSoundChannel() {
+// return 0;
+ for (int i=0; i<16; i++) {
+ if ( (SCHANNEL_CR(i) & SCHANNEL_ENABLE) == 0 ) return i;
+ }
+ return -1;
+}
+
+void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F, u8 pan=63, u8 format=0) {
+ REG_IME = IME_DISABLE;
+
+ channel = getFreeSoundChannel();
+/* if (format == 2) {
+ channel = 1;
+ } else {
+ channel = 0;
+ }*/
+
+ if (channel > 1) channel = 1;
+
+ bytes &= ~7; // Multiple of 4 bytes!
+// bytes += 4;
+
+ SCHANNEL_CR(channel) = 0;
+ SCHANNEL_TIMER(channel) = SOUND_FREQ(sampleRate);
+ SCHANNEL_SOURCE(channel) = ((uint32) (data));
+ SCHANNEL_LENGTH(channel) = ((bytes & 0x7FFFFFFF) >> 2);
+ SCHANNEL_REPEAT_POINT(channel) = 0;
+
+ SCHANNEL_CR(channel + 2) = 0;
+ SCHANNEL_TIMER(channel + 2) = SOUND_FREQ(sampleRate);
+ SCHANNEL_SOURCE(channel + 2) = ((uint32) (data));
+ SCHANNEL_LENGTH(channel + 2) = ((bytes & 0x7FFFFFFF) >> 2);
+ SCHANNEL_REPEAT_POINT(channel + 2) = 0;
+
+ uint32 flags = SCHANNEL_ENABLE | SOUND_VOL(vol) | SOUND_PAN(pan);
+
+ switch (format) {
+ case 1: {
+ flags |= SOUND_8BIT;
+ flags |= SOUND_REPEAT;// | (1 << 15);
+ break;
+ }
+
+ case 0: {
+ flags |= SOUND_16BIT;
+ flags |= SOUND_REPEAT;// | (1 << 15);
+ break;
+ }
+
+ case 2: {
+ flags |= SOUND_FORMAT_ADPCM;
+ flags |= SOUND_ONE_SHOT;// | (1 << 15);
+
+ SCHANNEL_SOURCE(channel) = (unsigned int) IPC->adpcm.buffer[0];
+ //bytes += 32;
+ SCHANNEL_LENGTH(channel) = (((bytes + 4) & 0x7FFFFFFF) >> 2);
+
+ SCHANNEL_CR(channel + 1) = 0;
+ SCHANNEL_SOURCE(channel + 1) = (unsigned int) IPC->adpcm.buffer[0];
+ SCHANNEL_LENGTH(channel + 1) = (((bytes + 4) & 0x7FFFFFFF) >> 2);
+ SCHANNEL_TIMER(channel + 1) = SOUND_FREQ(sampleRate);
+ SCHANNEL_REPEAT_POINT(channel + 1) = 0;
+ SCHANNEL_CR(channel + 1) = flags;
+ temp = bytes;
+ adpcmBufferNum = 0;
+ break;
+ }
+ }
+
+
+// if (bytes & 0x80000000) {
+// flags |= SOUND_REPEAT;
+// } else {
+// }
+
+
+
+
+ soundData = (vu8* ) data;
+
+ SCHANNEL_CR(channel) = flags;
+ SCHANNEL_CR(channel + 2) = flags;
+
+
+
+ if (channel == 0) {
+ for (volatile int i = 0; i < 16384 * 2; i++) {
+ // Delay loop - this makes everything stay in sync!
+ }
+
+ TIMER0_CR = 0;
+ TIMER0_DATA = SOUND_FREQ(sampleRate) * 2;
+ TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1;
+
+ TIMER1_CR = 0;
+ TIMER1_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3); // Trigger four times during the length of the buffer
+ TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+
+ playingSection = 0;
+ } else {
+ for (volatile int i = 0; i < 16384 * 2; i++) {
+ // Delay loop - this makes everything stay in sync!
+ }
+
+ TIMER2_CR = 0;
+ TIMER2_DATA = SOUND_FREQ(sampleRate) * 2;
+ TIMER2_CR = TIMER_ENABLE | TIMER_DIV_1;
+
+ TIMER3_CR = 0;
+ TIMER3_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3); // Trigger four times during the length of the buffer
+ TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+
+ for (int r = 0; r < 4; r++) {
+// IPC->streamFillNeeded[r] = true;
+ }
+
+ IPC->streamPlayingSection = 0;
+ }
+
+
+
+// IPC->fillSoundFirstHalf = true;
+// IPC->fillSoundSecondHalf = true;
+// soundFirstHalf = true;
+
+ REG_IME = IME_ENABLE;
+}
+
+void stopSound(int chan) {
+ SCHANNEL_CR(chan) = 0;
+}
+
+void DummyHandler() {
+ REG_IF = REG_IF;
+}
+
+uint16 powerManagerWrite(uint32 command, u32 data, bool enable) {
+
+ uint16 result;
+ SerialWaitBusy();
+
+ // Write the command and wait for it to complete
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11);
+ REG_SPIDATA = command | 0x80;
+ SerialWaitBusy();
+
+ // Write the second command and clock in the data
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz;
+ REG_SPIDATA = 0;
+ SerialWaitBusy();
+
+ result = REG_SPIDATA & 0xFF;
+
+
+
+ // Write the command and wait for it to complete
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11);
+ REG_SPIDATA = command;
+ SerialWaitBusy();
+
+ // Write the second command and clock in the data
+ REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz;
+ REG_SPIDATA = enable? (result | data): (result & ~data);
+ SerialWaitBusy();
+}
+
+/*
+void performSleep() {
+
+ powerManagerWrite(0, 0x30, true);
+
+ // Here, I set up a dummy interrupt handler, then trigger all interrupts.
+ // These are just aknowledged by the handler without doing anything else.
+ // Why? Because without it the sleep mode will only happen once, and then
+ // never again. I got the idea from reading the MoonShell source.
+ IME = 0;
+ u32 irq = (u32) IRQ_HANDLER;
+ IRQ_HANDLER = DummyHandler;
+ IF = ~0;
+ IME = 1;
+
+
+ // Now save which interrupts are enabled, then set only the screens unfolding
+ // interrupt to be enabled, so that the first interrupt that happens is the
+ // one I want.
+ int saveInts = IE;
+
+
+
+ IE = IRQ_TIMER0; // Screens unfolding interrupt
+
+ // Now call the sleep function in the bios
+ bool b;
+ do {
+ TIMER0_CR = 0;
+ TIMER0_DATA = TIMER_FREQ(20);
+ TIMER0_CR = TIMER_ENABLE | TIMER_DIV_64;
+
+ swiDelay(100);
+
+ swiSleep();
+
+ swiDelay(100);
+
+ powerManagerWrite(0, 0x30, b = !b);
+ } while (!(TIMER0_CR & TIMER_ENABLE));
+
+ TIMER0_CR = 0;
+
+ // We're back from sleep, now restore the interrupt state and IRQ handler
+ IRQ_HANDLER = (void (*)()) irq;
+ IE = saveInts;
+ IF = ~0;
+ IME = 1;
+
+
+
+ powerManagerWrite(0, 0x30, false);
+}
+*/
+
+
+void performSleep() {
+ powerManagerWrite(0, 0x30, true);
+
+ IPC->performArm9SleepMode = true; // Tell ARM9 to sleep
+
+// u32 irq = (u32) IRQ_HANDLER;
+// IRQ_HANDLER = DummyHandler;
+// POWER_CR &= ~POWER_SOUND;
+
+// int saveInts = REG_IE;
+// REG_IE = (1 << 22) | IRQ_VBLANK; // Lid open
+// *((u32*) (0x0380FFF8)) = *((u32*) (0x0380FFF8)) | (REG_IE & REG_IF);
+// VBLANK_INTR_WAIT_FLAGS = IRQ_VBLANK;
+
+
+ int r = 0;
+ while ((REG_KEYXY & (1 << 7))) { // Wait for lid to open
+ swiDelay(1000000);
+ r++;
+ }
+
+// IRQ_HANDLER = (void (*)()) irq;
+ IPC->performArm9SleepMode = false; // Tell ARM9 to wake up
+// REG_IE = saveInts;
+
+// POWER_CR |= POWER_SOUND;
+
+ powerManagerWrite(0, 0x30, false);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+
+void InterruptHandler(void) {
+ static int heartbeat = 0;
+
+ if (REG_IF & IRQ_TIMER1) {
+
+ IPC->fillNeeded[playingSection] = true;
+ soundFilled[playingSection] = false;
+
+ if (playingSection == 3) {
+// IME = IME_DISABLED;
+
+ // while (SCHANNEL_CR(0) & SCHANNEL_ENABLE) {
+ // }
+// SCHANNEL_CR(0) &= ~SCHANNEL_ENABLE;
+
+// SCHANNEL_CR(0) |= SCHANNEL_ENABLE;
+// TIMER1_CR = 0;
+// TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+
+ playingSection = 0;
+
+// IME = IME_ENABLED;
+ } else {
+ playingSection++;
+ }
+
+ IPC->playingSection = playingSection;
+
+/* for (int r = 0; r < 4; r++) {
+ //if ((!soundFilled[r]) && (!IPC->fillNeeded[playingSection])) {
+ memcpy((void *) (soundBuffer + (r * 1024)), (void *) (arm9Buffer + (r * 1024)), 1024);
+
+ vu16* p = (vu16 *) (soundBuffer);
+ //for (int t = 0; t < 2048; t++) {
+ // *(p + t) = (t & 1)? 0xF000: 0x0000;
+ //}
+ soundFilled[r] = true;
+ //}
+ }*/
+
+
+ REG_IF = IRQ_TIMER1;
+ }
+
+
+ if (REG_IF & IRQ_TIMER3) {
+ while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed
+ IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7
+
+ IPC->streamFillNeeded[IPC->streamPlayingSection] = true;
+
+ if (IPC->streamPlayingSection == 3) {
+ IPC->streamPlayingSection = 0;
+ } else {
+ IPC->streamPlayingSection++;
+ }
+
+ REG_IF = IRQ_TIMER3;
+
+ IPC->adpcm.semaphore = false;
+ }
+
+
+// IPC->performArm9SleepMode = false;
+
+ // precalculate some values
+// static int16 TOUCH_WIDTH = TOUCH_CAL_X2 - TOUCH_CAL_X1;
+// static int16 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1;
+// static int16 CNTRL_WIDTH = TOUCH_CNTRL_X2 - (TOUCH_CNTRL_X1 - 8);
+// static int16 CNTRL_HEIGHT = TOUCH_CNTRL_Y2 - (TOUCH_CNTRL_Y1 - 8);
+
+
+
+
+
+ if (REG_IF & IRQ_VBLANK) {
+ uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0, batt=0, aux=0;
+ int t1=0, t2=0;
+ uint32 temp=0;
+ uint8 ct[sizeof(IPC->curtime)];
+
+
+ // Update the heartbeat
+ heartbeat++;
+
+ // Read the X/Y buttons and the /PENIRQ line
+ but = REG_KEYXY;
+ if (!(but & 0x40)) {
+ // Read the touch screen
+ touchPosition p = touchReadXY();
+
+// x = touchRead(TSC_MEASURE_X);
+ // y = touchRead(TSC_MEASURE_Y);
+
+ x = p.x;
+ y = p.y;
+
+ xpx = p.px;
+ ypx = p.py;
+
+// xpx = ( ((SCREEN_WIDTH -60) * x) / TOUCH_WIDTH ) - TOUCH_OFFSET_X;
+ // ypx = ( ((SCREEN_HEIGHT-60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y;
+
+// xpx = (IPC->touchX - (int16) TOUCH_CAL_X1) * CNTRL_WIDTH / TOUCH_WIDTH + (int16) (TOUCH_CNTRL_X1 - 8);
+ // ypx = (IPC->touchY - (int16) TOUCH_CAL_Y1) * CNTRL_HEIGHT / TOUCH_HEIGHT + (int16) (TOUCH_CNTRL_Y1 - 8);
+
+
+ z1 = touchRead(TSC_MEASURE_Z1);
+ z2 = touchRead(TSC_MEASURE_Z2);
+ }
+
+ if (but & (1 << 7)) { // Check if screen is folded
+ needSleep = true;
+ }
+
+
+ batt = touchRead(TSC_MEASURE_BATTERY);
+ aux = touchRead(TSC_MEASURE_AUX);
+
+ // Read the time
+ rtcGetTime((uint8 *)ct);
+ BCDToInteger((uint8 *)&(ct[1]), 7);
+
+ // Read the temperature
+ temp = touchReadTemperature(&t1, &t2);
+
+ // Update the IPC struct
+ IPC->heartbeat = heartbeat;
+ IPC->buttons = but;
+ IPC->touchX = x;
+ IPC->touchY = y;
+ IPC->touchXpx = xpx;
+ IPC->touchYpx = ypx;
+ IPC->touchZ1 = z1;
+ IPC->touchZ2 = z2;
+ IPC->battery = batt;
+ IPC->aux = aux;
+
+ for(u32 i=0; i<sizeof(ct); i++) {
+ IPC->curtime[i] = ct[i];
+ }
+
+ IPC->temperature = temp;
+ IPC->tdiode1 = t1;
+ IPC->tdiode2 = t2;
+
+
+
+ //sound code :)
+ TransferSound *snd = IPC->soundData;
+ IPC->soundData = 0;
+ if (snd) {
+ for (int i=0; i<snd->count; i++) {
+ s8 chan = getFreeSoundChannel();
+ if (snd->data[i].rate > 0) {
+ if (chan >= 0) {
+ startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format);
+ }
+ } else {
+ stopSound(-snd->data[i].rate);
+ }
+ }
+ }
+
+ REG_IF = IRQ_VBLANK;
+ }
+
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+int main(int argc, char ** argv) {
+ // Reset the clock if needed
+ rtcReset();
+
+ //enable sound
+ powerON(POWER_SOUND);
+ SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F);
+ IPC->soundData = 0;
+ IPC->reset = false;
+
+
+ for (int r = 0; r < 8; r++) {
+ IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512);
+ }
+
+ for (int r = 0; r < 4; r++) {
+ soundFilled[r] = false;
+ }
+
+
+ // Set up the interrupt handler
+ REG_IME = 0;
+ IRQ_HANDLER = &InterruptHandler;
+ REG_IE = IRQ_VBLANK | IRQ_TIMER1 | IRQ_TIMER3;
+ REG_IF = ~0;
+ DISP_SR = DISP_VBLANK_IRQ;
+ REG_IME = 1;
+
+
+
+ // Keep the ARM7 out of main RAM
+ while (1) {
+ if (needSleep) {
+ performSleep();
+ needSleep = false;
+ }
+// if (IPC->reset) {
+// swiSoftReset();
+// }
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
diff --git a/backends/platform/ds/arm9/buildkeyboard.bat b/backends/platform/ds/arm9/buildkeyboard.bat
new file mode 100644
index 0000000000..2078381ccb
--- /dev/null
+++ b/backends/platform/ds/arm9/buildkeyboard.bat
@@ -0,0 +1,8 @@
+cd data
+..\tools\gfx2gba -c16 -t8 -M -pkeyboard_pal.raw ..\keyboard.bmp
+del keyboard.map
+pause
+cd data
+..\tools\gfx2gba -c16 -t8 -M -pkeyboard_pal.raw ..\keyboard.bmp
+del keyboard.map
+pause \ No newline at end of file
diff --git a/backends/platform/ds/arm9/lib/readme.txt b/backends/platform/ds/arm9/lib/readme.txt
new file mode 100644
index 0000000000..f6e7f4c511
--- /dev/null
+++ b/backends/platform/ds/arm9/lib/readme.txt
@@ -0,0 +1,2 @@
+If building with MAD enabled, put libmad.a in here.
+If building with MAD enabled, put libmad.a in here. \ No newline at end of file
diff --git a/backends/platform/ds/arm9/makefile b/backends/platform/ds/arm9/makefile
new file mode 100644
index 0000000000..c648d6d4dd
--- /dev/null
+++ b/backends/platform/ds/arm9/makefile
@@ -0,0 +1,522 @@
+#BUILD_PLUGINS = 1
+libndsdir = $(DEVKITPRO)/libnds
+
+DS_SCUMM_BUILD = 1
+
+#DS_NON_SCUMM_BUILD = 1
+
+USE_MAD = 1
+
+VPATH = $(srcdir)
+
+# Command to build libmad is:
+# ./configure --host=arm-elf --enable-speed --enable-sso -enable-fpm=arm CFLAGS='-specs=ds_arm9.specs -mthumb-interwork'
+
+ifdef DS_NON_SCUMM_BUILD
+ DEFINES = -DDS_NON_SCUMM_BUILD
+ DISABLE_HE = 1
+ DISABLE_SCUMM = 1
+ DISABLE_SCUMM_7_8 = 1
+ #DISABLE_SIMON = 1
+ #DISABLE_SKY = 1
+ DISABLE_SWORD1 = 1
+ DISABLE_SWORD2 = 1
+ #DISABLE_QUEEN = 1
+ DISABLE_SAGA = 1
+ DISABLE_KYRA = 1
+ #DISABLE_GOB = 1
+ DISABLE_LURE = 1
+ DISABLE_CINE = 1
+ DISABLE_AGI = 1
+ BUILD=scummvm-B
+endif
+
+ifdef DS_SCUMM_BUILD
+ DEFINES = -DDS_SCUMM_BUILD
+ DISABLE_HE = 1
+ #DISABLE_SCUMM = 1
+ DISABLE_SCUMM_7_8 = 1
+ DISABLE_SIMON = 1
+ DISABLE_SKY = 1
+ DISABLE_SWORD1 = 1
+ DISABLE_SWORD2 = 1
+ DISABLE_QUEEN = 1
+ DISABLE_SAGA = 1
+ DISABLE_KYRA = 1
+ DISABLE_GOB = 1
+ DISABLE_LURE = 1
+ DISABLE_CINE = 1
+ DISABLE_AGI = 1
+ BUILD=scummvm-A
+endif
+
+ARM7BIN := -7 $(CURDIR)/../../arm7/arm7.bin
+ICON := -b ../../../logo.bmp "ScummVM;By Neil Millstone;"
+
+CC = arm-eabi-gcc
+CXX = arm-eabi-g++
+
+CFLAGS = -Wno-multichar -Wall -Os\
+ -Wno-multichar -mcpu=arm9tdmi -mtune=arm9tdmi \
+ -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer\
+ -ffast-math -mthumb-interwork
+
+CXXFLAGS= $(CFLAGS) -Wno-non-virtual-dtor -Wno-non-virtual-dtor \
+ -fno-exceptions -fno-rtti
+
+ASFLAGS = -mcpu=arm9tdmi -mthumb-interwork
+DEFINES += -D__DS__ -DNDS -DARM9 -DNONSTANDARD_PORT -DDISABLE_FANCY_THEMES
+ifdef USE_MAD
+ DEFINES += -DUSE_MAD
+endif
+
+LDFLAGS = -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt
+
+INCLUDES= -I./ -I$(portdir)/$(BUILD) -I$(srcdir) -I$(srcdir)/common -I$(portdir)/source -I$(portdir)/source/compressor -I$(portdir)/source/fat \
+ -I$(srcdir)/backends/fs -I$(srcdir)/backends/fs/ds -I$(portdir)/data -I$(libndsdir)/include -I$(portdir)/../commoninclude\
+ -I$(srcdir)/scumm -I$(libndsdir)/include -I$(libndsdir)/include/nds -I$(srcdir)/engines -I$(portdir)/source/mad
+
+
+LIBS = -lm -L$(libndsdir)/lib -L$(portdir)/lib -lnds9
+ifdef USE_MAD
+ LIBS += -lmad
+endif
+
+#-Lscumm -lscumm -Lbase -lbase -Lcommon -lcommon -Lgraphics -lgraphics -Lgui -lgui -Lsound -lsound
+EXECUTABLE = scummvm.elf
+PLUGIN_PREFIX =
+PLUGIN_SUFFIX = .plg
+PLUGIN_EXTRA_DEPS = plugin.x plugin.syms scummvm.elf
+PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,-Tplugin.x,--just-symbols,scummvm.elf,--retain-symbols-file,plugin.syms -L$(ronindir)/lib
+MKDIR = mkdir -p
+RM = rm -f
+RM_REC = rm -rf
+AR = arm-eabi-ar cru
+RANLIB = arm-eabi-ranlib
+OBJCOPY = arm-eabi-objcopy
+AS = arm-eabi-as
+HAVE_GCC3 = true
+DISABLE_SCALERS = true
+
+ifdef BUILD_PLUGINS
+DEFINES += -DDYNAMIC_MODULES
+PRE_OBJS_FLAGS = -Wl,--whole-archive
+POST_OBJS_FLAGS = -Wl,--no-whole-archive
+endif
+
+PORT_OBJS := $(portdir)/source/blitters.o $(portdir)/source/cdaudio.o $(portdir)/source/dsmain.o \
+ $(portdir)/../../../fs/ds/ds-fs.o $(portdir)/source/gbampsave.o $(portdir)/source/scummhelp.o\
+ $(portdir)/source/osystem_ds.o $(portdir)/source/portdefs.o $(portdir)/source/ramsave.o\
+ $(portdir)/source/scummconsole.o $(portdir)/source/touchkeyboard.o $(portdir)/source/zipreader.o\
+ $(portdir)/source/dsoptions.o
+
+DATA_OBJS := $(portdir)/data/icons.o $(portdir)/data/keyboard.o $(portdir)/data/keyboard_pal.o $(portdir)/data/default_font.o
+
+
+COMPRESSOR_OBJS := $(portdir)/source/compressor/lz.o
+
+FAT_OBJS := $(portdir)/source/fat/disc_io.o $(portdir)/source/fat/gba_nds_fat.o\
+ $(portdir)/source/fat/io_fcsr.o $(portdir)/source/fat/io_m3cf.o\
+ $(portdir)/source/fat/io_mpcf.o $(portdir)/source/fat/io_sccf.o\
+ $(portdir)/source/fat/io_m3sd.o\
+ $(portdir)/source/fat/io_nmmc.o $(portdir)/source/fat/io_scsd.o \
+ $(portdir)/source/fat/io_m3sd_asm.o $(portdir)/source/fat/io_scsd_asm.o
+
+
+
+OBJS := $(DATA_OBJS) $(PORT_OBJS) $(COMPRESSOR_OBJS) $(FAT_OBJS)
+
+
+
+MODULE_DIRS += .
+
+ndsall:
+ @[ -d $(BUILD) ] || mkdir -p $(BUILD)
+ make -C ./$(BUILD) -f ../makefile scummvm.nds
+
+include $(srcdir)/Makefile.common
+
+clean:
+ $(RM) $(OBJS) $(EXECUTABLE)
+ rm -fr $(BUILD)
+
+plugin_dist :
+ find . -name '*.plg' | while read p; do \
+ sh-elf-strip -g -o "`basename \"$$p\" | tr '[:lower:]' '[:upper:]'`" "$$p"; \
+ done
+
+dist : SCUMMVM.BIN plugins plugin_dist
+
+
+#---------------------------------------------------------------------------------
+# canned command sequence for binary data
+#---------------------------------------------------------------------------------
+#define bin2o
+# bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@)
+# echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw_end[];" > `(echo $(<F) | tr . _)`.h
+# echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw[];" >> `(echo $(<F) | tr . _)`.h
+# echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_raw_size";" >> `(echo $(<F) | tr . _)`.h
+#endef
+
+
+define bin2o
+ bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@)
+ echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
+ echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
+ echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
+endef
+
+
+#define bin2o
+# @echo $(*)
+# cp $(<) $(*).tmp
+# $(OBJCOPY) -I binary -O elf32-littlearm -B arm \
+# --rename-section .data=.rodata \
+# --redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_start=$(notdir $*)\
+# --redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_end=$(notdir $*)_end\
+# $(*).tmp $(@)
+# echo "extern const u8" $(notdir $*)"[] __attribute__ ((aligned (4)));" > $(*).h
+# echo "extern const u32" $(notdir $(*))_size[]";" >> $(*).h
+#
+# echo $(*).h
+# rm $(*).tmp
+#endef
+
+##############
+# Replacement rule for the one in makefile.common
+##############
+ifndef HAVE_GCC3
+# If you use GCC, disable the above and enable this for intelligent
+# dependency tracking.
+.cpp.o:
+ $(MKDIR) $(*D)/$(DEPDIR)
+ $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+ $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d
+ $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d"
+ $(RM) "$(*D)/$(DEPDIR)/$(*F).d2"
+else
+# If you even have GCC 3.x, you can use this build rule, which is safer; the above
+# rule can get you into a bad state if you Ctrl-C at the wrong moment.
+# Also, with this GCC inserts additional dummy rules for the involved headers,
+# which ensures a smooth compilation even if said headers become obsolete.
+.cpp.o:
+ $(MKDIR) $(*D)/$(DEPDIR)
+# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+ $(CXX) -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+endif
+
+
+
+#---------------------------------------------------------------------------------
+%.o : %.pcx
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.raw
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.pal
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.map
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.mdl
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.nds: %.bin
+ @echo ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6"
+ ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6"
+ dsbuild $@
+ padbin 16 $(basename $@).ds.gba
+
+#---------------------------------------------------------------------------------
+%.bin: %.elf
+ $(OBJCOPY) -S scummvm.elf scummvm-stripped.elf
+ $(OBJCOPY) -O binary scummvm-stripped.elf scummvm.bin
+
+#%.o: %.s
+# $(MKDIR) $(*D)/$(DEPDIR)
+# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+
+#BUILD_PLUGINS = 1
+libndsdir = $(DEVKITPRO)/libnds
+
+DS_SCUMM_BUILD = 1
+
+#DS_NON_SCUMM_BUILD = 1
+
+USE_MAD = 1
+
+VPATH = $(srcdir)
+
+# Command to build libmad is:
+# ./configure --host=arm-elf --enable-speed --enable-sso -enable-fpm=arm CFLAGS='-specs=ds_arm9.specs -mthumb-interwork'
+
+ifdef DS_NON_SCUMM_BUILD
+ DEFINES = -DDS_NON_SCUMM_BUILD
+ DISABLE_HE = 1
+ DISABLE_SCUMM = 1
+ DISABLE_SCUMM_7_8 = 1
+ #DISABLE_SIMON = 1
+ #DISABLE_SKY = 1
+ DISABLE_SWORD1 = 1
+ DISABLE_SWORD2 = 1
+ #DISABLE_QUEEN = 1
+ DISABLE_SAGA = 1
+ DISABLE_KYRA = 1
+ #DISABLE_GOB = 1
+ DISABLE_LURE = 1
+ DISABLE_CINE = 1
+ DISABLE_AGI = 1
+ BUILD=scummvm-B
+endif
+
+ifdef DS_SCUMM_BUILD
+ DEFINES = -DDS_SCUMM_BUILD
+ DISABLE_HE = 1
+ #DISABLE_SCUMM = 1
+ DISABLE_SCUMM_7_8 = 1
+ DISABLE_SIMON = 1
+ DISABLE_SKY = 1
+ DISABLE_SWORD1 = 1
+ DISABLE_SWORD2 = 1
+ DISABLE_QUEEN = 1
+ DISABLE_SAGA = 1
+ DISABLE_KYRA = 1
+ DISABLE_GOB = 1
+ DISABLE_LURE = 1
+ DISABLE_CINE = 1
+ DISABLE_AGI = 1
+ BUILD=scummvm-A
+endif
+
+ARM7BIN := -7 $(CURDIR)/../../arm7/arm7.bin
+ICON := -b ../../../logo.bmp "ScummVM;By Neil Millstone;"
+
+CC = arm-eabi-gcc
+CXX = arm-eabi-g++
+
+CFLAGS = -Wno-multichar -Wall -Os\
+ -Wno-multichar -mcpu=arm9tdmi -mtune=arm9tdmi \
+ -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer\
+ -ffast-math -mthumb-interwork
+
+CXXFLAGS= $(CFLAGS) -Wno-non-virtual-dtor -Wno-non-virtual-dtor \
+ -fno-exceptions -fno-rtti
+
+ASFLAGS = -mcpu=arm9tdmi -mthumb-interwork
+DEFINES += -D__DS__ -DNDS -DARM9 -DNONSTANDARD_PORT -DDISABLE_FANCY_THEMES
+ifdef USE_MAD
+ DEFINES += -DUSE_MAD
+endif
+
+LDFLAGS = -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt
+
+INCLUDES= -I./ -I$(portdir)/$(BUILD) -I$(srcdir) -I$(srcdir)/common -I$(portdir)/source -I$(portdir)/source/compressor -I$(portdir)/source/fat \
+ -I$(srcdir)/backends/fs -I$(srcdir)/backends/fs/ds -I$(portdir)/data -I$(libndsdir)/include -I$(portdir)/../commoninclude\
+ -I$(srcdir)/scumm -I$(libndsdir)/include -I$(libndsdir)/include/nds -I$(srcdir)/engines -I$(portdir)/source/mad
+
+
+LIBS = -lm -L$(libndsdir)/lib -L$(portdir)/lib -lnds9
+ifdef USE_MAD
+ LIBS += -lmad
+endif
+
+#-Lscumm -lscumm -Lbase -lbase -Lcommon -lcommon -Lgraphics -lgraphics -Lgui -lgui -Lsound -lsound
+EXECUTABLE = scummvm.elf
+PLUGIN_PREFIX =
+PLUGIN_SUFFIX = .plg
+PLUGIN_EXTRA_DEPS = plugin.x plugin.syms scummvm.elf
+PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,-Tplugin.x,--just-symbols,scummvm.elf,--retain-symbols-file,plugin.syms -L$(ronindir)/lib
+MKDIR = mkdir -p
+RM = rm -f
+RM_REC = rm -rf
+AR = arm-eabi-ar cru
+RANLIB = arm-eabi-ranlib
+OBJCOPY = arm-eabi-objcopy
+AS = arm-eabi-as
+HAVE_GCC3 = true
+DISABLE_SCALERS = true
+
+ifdef BUILD_PLUGINS
+DEFINES += -DDYNAMIC_MODULES
+PRE_OBJS_FLAGS = -Wl,--whole-archive
+POST_OBJS_FLAGS = -Wl,--no-whole-archive
+endif
+
+PORT_OBJS := $(portdir)/source/blitters.o $(portdir)/source/cdaudio.o $(portdir)/source/dsmain.o \
+ $(portdir)/../../../fs/ds/ds-fs.o $(portdir)/source/gbampsave.o $(portdir)/source/scummhelp.o\
+ $(portdir)/source/osystem_ds.o $(portdir)/source/portdefs.o $(portdir)/source/ramsave.o\
+ $(portdir)/source/scummconsole.o $(portdir)/source/touchkeyboard.o $(portdir)/source/zipreader.o\
+ $(portdir)/source/dsoptions.o
+
+DATA_OBJS := $(portdir)/data/icons.o $(portdir)/data/keyboard.o $(portdir)/data/keyboard_pal.o $(portdir)/data/default_font.o
+
+
+COMPRESSOR_OBJS := $(portdir)/source/compressor/lz.o
+
+FAT_OBJS := $(portdir)/source/fat/disc_io.o $(portdir)/source/fat/gba_nds_fat.o\
+ $(portdir)/source/fat/io_fcsr.o $(portdir)/source/fat/io_m3cf.o\
+ $(portdir)/source/fat/io_mpcf.o $(portdir)/source/fat/io_sccf.o\
+ $(portdir)/source/fat/io_m3sd.o\
+ $(portdir)/source/fat/io_nmmc.o $(portdir)/source/fat/io_scsd.o \
+ $(portdir)/source/fat/io_m3sd_asm.o $(portdir)/source/fat/io_scsd_asm.o
+
+
+
+OBJS := $(DATA_OBJS) $(PORT_OBJS) $(COMPRESSOR_OBJS) $(FAT_OBJS)
+
+
+
+MODULE_DIRS += .
+
+ndsall:
+ @[ -d $(BUILD) ] || mkdir -p $(BUILD)
+ make -C ./$(BUILD) -f ../makefile scummvm.nds
+
+include $(srcdir)/Makefile.common
+
+clean:
+ $(RM) $(OBJS) $(EXECUTABLE)
+ rm -fr $(BUILD)
+
+plugin_dist :
+ find . -name '*.plg' | while read p; do \
+ sh-elf-strip -g -o "`basename \"$$p\" | tr '[:lower:]' '[:upper:]'`" "$$p"; \
+ done
+
+dist : SCUMMVM.BIN plugins plugin_dist
+
+
+#---------------------------------------------------------------------------------
+# canned command sequence for binary data
+#---------------------------------------------------------------------------------
+#define bin2o
+# bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@)
+# echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw_end[];" > `(echo $(<F) | tr . _)`.h
+# echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw[];" >> `(echo $(<F) | tr . _)`.h
+# echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_raw_size";" >> `(echo $(<F) | tr . _)`.h
+#endef
+
+
+define bin2o
+ bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@)
+ echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
+ echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
+ echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
+endef
+
+
+#define bin2o
+# @echo $(*)
+# cp $(<) $(*).tmp
+# $(OBJCOPY) -I binary -O elf32-littlearm -B arm \
+# --rename-section .data=.rodata \
+# --redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_start=$(notdir $*)\
+# --redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_end=$(notdir $*)_end\
+# $(*).tmp $(@)
+# echo "extern const u8" $(notdir $*)"[] __attribute__ ((aligned (4)));" > $(*).h
+# echo "extern const u32" $(notdir $(*))_size[]";" >> $(*).h
+#
+# echo $(*).h
+# rm $(*).tmp
+#endef
+
+##############
+# Replacement rule for the one in makefile.common
+##############
+ifndef HAVE_GCC3
+# If you use GCC, disable the above and enable this for intelligent
+# dependency tracking.
+.cpp.o:
+ $(MKDIR) $(*D)/$(DEPDIR)
+ $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+ $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d
+ $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d"
+ $(RM) "$(*D)/$(DEPDIR)/$(*F).d2"
+else
+# If you even have GCC 3.x, you can use this build rule, which is safer; the above
+# rule can get you into a bad state if you Ctrl-C at the wrong moment.
+# Also, with this GCC inserts additional dummy rules for the involved headers,
+# which ensures a smooth compilation even if said headers become obsolete.
+.cpp.o:
+ $(MKDIR) $(*D)/$(DEPDIR)
+# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+ $(CXX) -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+endif
+
+
+
+#---------------------------------------------------------------------------------
+%.o : %.pcx
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.raw
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.pal
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.map
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.o : %.mdl
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.nds: %.bin
+ @echo ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6"
+ ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6"
+ dsbuild $@
+ padbin 16 $(basename $@).ds.gba
+
+#---------------------------------------------------------------------------------
+%.bin: %.elf
+ $(OBJCOPY) -S scummvm.elf scummvm-stripped.elf
+ $(OBJCOPY) -O binary scummvm-stripped.elf scummvm.bin
+
+#%.o: %.s
+# $(MKDIR) $(*D)/$(DEPDIR)
+# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
+ \ No newline at end of file
diff --git a/backends/platform/ds/arm9/source/blitters.cpp b/backends/platform/ds/arm9/source/blitters.cpp
new file mode 100644
index 0000000000..5c9e07fd47
--- /dev/null
+++ b/backends/platform/ds/arm9/source/blitters.cpp
@@ -0,0 +1,272 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "stdafx.h"
+#define CHARSET_MASK_TRANSPARENCY 253
+
+namespace DS {
+
+void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst,
+ int vsPitch, int vmScreenWidth, int textSurfacePitch) {
+
+
+ if (height <= 0) height = 1;
+ if (width < 4) return;
+
+
+ width &= ~4;
+// src = (const byte *) (((int) (src)) & (~4));
+// dst = (byte *) (((int) (dst)) & (~4));
+// text = (const byte *) (((int) (text)) & (~4));
+
+ asm ( "mov r5, %0\n" // Height
+ "yLoop:\n"
+ "mov r3, #0\n" // X pos
+
+ "xLoop:\n"
+
+ "ldr r4, [%2, r3]\n" // Load text layer word
+ "cmp r4, %5\n"
+ "bne singleByteCompare\n"
+ "ldr r4, [%3, r3]\n"
+ "str r4, [%4, r3]\n"
+ "add r3, r3, #4\n"
+ "cmp r3, %1\n" // x == width?
+ "blt xLoop\n"
+
+ "add %2, %2, %8\n" // src += vs->pitch
+ "add %3, %3, %6\n" // dst += _vm->_screenWidth
+ "add %4, %4, %7\n" // text += _textSurface.pitch
+ "sub r5, r5, #1\n" // y -= 1
+ "cmp r5, #0\n" // y == 0?
+ "bne yLoop\n"
+ "b end\n"
+
+
+ "singleByteCompare:\n"
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "cmps r3, %1\n" // x == width?
+ "blt xLoop\n" // Repeat
+ "add %2, %2, %8\n" // src += vs->pitch
+ "add %3, %3, %6\n" // dst += _vm->_screenWidth
+ "add %4, %4, %7\n" // text += _textSurface.pitch
+ "sub r5, r5, #1\n" // y -= 1
+ "cmp r5, #0\n" // y == 0?
+ "bne yLoop\n"
+
+ "end:\n"
+ : /* no output registers */
+ : "r" (height), "r" (width), "r" (text), "r" (src), "r" (dst), "r" (CHARSET_MASK_TRANSPARENCY | (CHARSET_MASK_TRANSPARENCY << 8) | (CHARSET_MASK_TRANSPARENCY << 16) | (CHARSET_MASK_TRANSPARENCY << 24)),
+ "r" (vsPitch), "r" (vmScreenWidth), "r" (textSurfacePitch)
+ : "r5", "r3", "r4", "%2", "%3", "%4", "memory");
+}
+
+
+
+void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height) {
+ asm("ands r0, %3, #1\n"
+ "addne %3, %3, #1\n"
+ "bne roll2\n"
+
+ "yLoop2:\n"
+ "ldr r0, [%2, #0]\n"
+ "str r0, [%0, #0]\n"
+ "ldr r0, [%2, #4]\n"
+ "str r0, [%0, #4]\n"
+ "add %0, %0, %1\n"
+ "add %2, %2, %1\n"
+ "roll2:\n"
+ "ldr r0, [%2, #0]\n"
+ "str r0, [%0, #0]\n"
+ "ldr r0, [%2, #4]\n"
+ "str r0, [%0, #4]\n"
+ "add %0, %0, %1\n"
+ "add %2, %2, %1\n"
+ "subs %3, %3, #2\n"
+ "bne yLoop2\n"
+
+ : /* no output registers */
+ : "r" (dst), "r" (dstPitch), "r" (src), "r" (height)
+ : "r0", "%0", "%2", "%3");
+}
+
+}
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "stdafx.h"
+#define CHARSET_MASK_TRANSPARENCY 253
+
+namespace DS {
+
+void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst,
+ int vsPitch, int vmScreenWidth, int textSurfacePitch) {
+
+
+ if (height <= 0) height = 1;
+ if (width < 4) return;
+
+
+ width &= ~4;
+// src = (const byte *) (((int) (src)) & (~4));
+// dst = (byte *) (((int) (dst)) & (~4));
+// text = (const byte *) (((int) (text)) & (~4));
+
+ asm ( "mov r5, %0\n" // Height
+ "yLoop:\n"
+ "mov r3, #0\n" // X pos
+
+ "xLoop:\n"
+
+ "ldr r4, [%2, r3]\n" // Load text layer word
+ "cmp r4, %5\n"
+ "bne singleByteCompare\n"
+ "ldr r4, [%3, r3]\n"
+ "str r4, [%4, r3]\n"
+ "add r3, r3, #4\n"
+ "cmp r3, %1\n" // x == width?
+ "blt xLoop\n"
+
+ "add %2, %2, %8\n" // src += vs->pitch
+ "add %3, %3, %6\n" // dst += _vm->_screenWidth
+ "add %4, %4, %7\n" // text += _textSurface.pitch
+ "sub r5, r5, #1\n" // y -= 1
+ "cmp r5, #0\n" // y == 0?
+ "bne yLoop\n"
+ "b end\n"
+
+
+ "singleByteCompare:\n"
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "ldrb r4, [%2, r3]\n" // Load text byte
+ "cmps r4, %5, lsr #24\n" // Compare with mask
+ "strneb r4, [%4, r3]\n" // Store if not equal
+ "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte
+ "streqb r4, [%4, r3]\n" // Store it
+ "add r3, r3, #1\n"
+
+ "cmps r3, %1\n" // x == width?
+ "blt xLoop\n" // Repeat
+ "add %2, %2, %8\n" // src += vs->pitch
+ "add %3, %3, %6\n" // dst += _vm->_screenWidth
+ "add %4, %4, %7\n" // text += _textSurface.pitch
+ "sub r5, r5, #1\n" // y -= 1
+ "cmp r5, #0\n" // y == 0?
+ "bne yLoop\n"
+
+ "end:\n"
+ : /* no output registers */
+ : "r" (height), "r" (width), "r" (text), "r" (src), "r" (dst), "r" (CHARSET_MASK_TRANSPARENCY | (CHARSET_MASK_TRANSPARENCY << 8) | (CHARSET_MASK_TRANSPARENCY << 16) | (CHARSET_MASK_TRANSPARENCY << 24)),
+ "r" (vsPitch), "r" (vmScreenWidth), "r" (textSurfacePitch)
+ : "r5", "r3", "r4", "%2", "%3", "%4", "memory");
+}
+
+
+
+void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height) {
+ asm("ands r0, %3, #1\n"
+ "addne %3, %3, #1\n"
+ "bne roll2\n"
+
+ "yLoop2:\n"
+ "ldr r0, [%2, #0]\n"
+ "str r0, [%0, #0]\n"
+ "ldr r0, [%2, #4]\n"
+ "str r0, [%0, #4]\n"
+ "add %0, %0, %1\n"
+ "add %2, %2, %1\n"
+ "roll2:\n"
+ "ldr r0, [%2, #0]\n"
+ "str r0, [%0, #0]\n"
+ "ldr r0, [%2, #4]\n"
+ "str r0, [%0, #4]\n"
+ "add %0, %0, %1\n"
+ "add %2, %2, %1\n"
+ "subs %3, %3, #2\n"
+ "bne yLoop2\n"
+
+ : /* no output registers */
+ : "r" (dst), "r" (dstPitch), "r" (src), "r" (height)
+ : "r0", "%0", "%2", "%3");
+}
+
+}
diff --git a/backends/platform/ds/arm9/source/blitters.h b/backends/platform/ds/arm9/source/blitters.h
new file mode 100644
index 0000000000..3b0ee5e4c5
--- /dev/null
+++ b/backends/platform/ds/arm9/source/blitters.h
@@ -0,0 +1,62 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+ #ifndef _BLITTERS_H_
+ #define _BLITTERS_H_
+
+namespace DS {
+
+void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst,
+ int vsPitch, int vmScreenWidth, int textSurfacePitch);
+void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height);
+
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+ #ifndef _BLITTERS_H_
+ #define _BLITTERS_H_
+
+namespace DS {
+
+void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst,
+ int vsPitch, int vmScreenWidth, int textSurfacePitch);
+void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height);
+
+}
+
+#endif
diff --git a/backends/platform/ds/arm9/source/cdaudio.cpp b/backends/platform/ds/arm9/source/cdaudio.cpp
new file mode 100644
index 0000000000..a6e4455b22
--- /dev/null
+++ b/backends/platform/ds/arm9/source/cdaudio.cpp
@@ -0,0 +1,968 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "stdafx.h"
+#include "cdaudio.h"
+#include "ds-fs.h"
+#include "config-manager.h"
+#include "dsmain.h"
+#include "nds/scummvm_ipc.h"
+#include "console2.h"
+
+#define WAV_FORMAT_IMA_ADPCM 0x14
+#define BUFFER_SIZE 8192
+#define BUFFER_CHUNK_SIZE (BUFFER_SIZE >> 2)
+
+namespace DS {
+namespace CD {
+
+struct WaveHeader {
+
+ char riff[4]; // 'RIFF'
+ u32 size; // Size of the file
+ char wave[4]; // 'WAVE'
+
+ // fmt chunk
+ char fmt[4]; // 'fmt '
+ u32 fmtSize; // Chunk size
+ u16 fmtFormatTag; // Format of this file
+ u16 fmtChannels; // Num channels
+ u32 fmtSamPerSec; // Samples per second
+ u32 fmtBytesPerSec; // Bytes per second
+ u16 fmtBlockAlign; // Block alignment
+ u16 fmtBitsPerSam; // Bits per sample
+
+ u16 fmtExtraData; // Number of extra fmt bytes
+ u16 fmtExtra; // Samples per block (only for IMA-ADPCM files)
+} __attribute__ ((packed));
+
+struct chunkHeader {
+ char name[4];
+ u32 size;
+} __attribute__ ((packed));
+
+struct Header {
+ s16 firstSample;
+ char stepTableIndex;
+ char reserved;
+} __attribute__ ((packed));
+
+struct decoderFormat {
+ s16 initial;
+ unsigned char tableIndex;
+ unsigned char test;
+ unsigned char sample[1024];
+} __attribute__ ((packed));
+
+bool active = false;
+WaveHeader waveHeader;
+Header blockHeader;
+FILE* file;
+int fillPos;
+bool isPlayingFlag = false;
+
+s16* audioBuffer;
+u32 sampleNum;
+s16* decompressionBuffer;
+int numLoops;
+int blockCount;
+int dataChunkStart;
+int blocksLeft;
+
+
+// These are from Microsoft's document on DVI ADPCM
+const int stepTab[ 89 ] = {
+7, 8, 9, 10, 11, 12, 13, 14,
+16, 17, 19, 21, 23, 25, 28, 31,
+34, 37, 41, 45, 50, 55, 60, 66,
+73, 80, 88, 97, 107, 118, 130, 143,
+157, 173, 190, 209, 230, 253, 279, 307,
+337, 371, 408, 449, 494, 544, 598, 658,
+724, 796, 876, 963, 1060, 1166, 1282, 1411,
+1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+32767 };
+
+const int indexTab[ 16 ] = { -1, -1, -1, -1, 2, 4, 6, 8,
+-1, -1, -1, -1, 2, 4, 6, 8 };
+
+void playNextBlock();
+void decompressBlock();
+
+
+void allocBuffers() {
+
+}
+
+void setActive(bool active) {
+ active = active;
+}
+
+bool getActive() {
+ return active;
+}
+
+void playTrack(int track, int numLoops, int startFrame, int duration) {
+ Common::String path = ConfMan.get("path");
+
+ if (isPlayingFlag) {
+ stopTrack();
+ }
+
+
+
+
+
+ char str[100];
+ sprintf(str, "/track%d.wav", track);
+ path = path + str;
+
+
+ //1820160
+
+ file = DS::std_fopen(path.c_str(), "rb");
+
+ if (!file) {
+ consolePrintf("Failed to open %s!\n", path.c_str());
+ return;
+ }
+
+
+ DS::std_fread((const void *) &waveHeader, sizeof(waveHeader), 1, file);
+
+ consolePrintf("Playing track %d\n", track);
+ consolePrintf("Format: %d\n", waveHeader.fmtFormatTag);
+ consolePrintf("Rate : %d\n", waveHeader.fmtSamPerSec);
+ consolePrintf("Bits : %d\n", waveHeader.fmtBitsPerSam);
+ consolePrintf("BlkSz : %d\n", waveHeader.fmtExtra);
+
+ if ((waveHeader.fmtFormatTag != 17) && (waveHeader.fmtFormatTag != 20)) {
+ consolePrintf("Wave file is in the wrong format! You must use IMA-ADPCM 4-bit mono.\n");
+ return;
+ }
+
+ for (int r = 0; r < 8; r++) {
+ IPC->adpcm.buffer[r] = (u8 * volatile) (decoderFormat *) malloc(waveHeader.fmtBlockAlign);
+ IPC->adpcm.filled[r] = false;
+ IPC->adpcm.arm7Dirty[r] = false;
+ }
+
+ // Skip chunks until we reach the data chunk
+ chunkHeader chunk;
+ DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file);
+
+ while (!((chunk.name[0] == 'd') && (chunk.name[1] == 'a') && (chunk.name[2] == 't') && (chunk.name[3] == 'a'))) {
+ DS::std_fseek(file, chunk.size, SEEK_CUR);
+ DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file);
+ }
+
+ dataChunkStart = DS::std_ftell(file);
+
+
+ static bool started = false;
+ sampleNum = 0;
+ blockCount = 0;
+
+ IPC->streamFillNeeded[0] = true;
+ IPC->streamFillNeeded[1] = true;
+ IPC->streamFillNeeded[2] = true;
+ IPC->streamFillNeeded[3] = true;
+ if (!started) {
+ fillPos = 0;
+ audioBuffer = (s16 *) malloc(BUFFER_SIZE * 2);
+ decompressionBuffer = (s16 *) malloc(waveHeader.fmtExtra * 2);
+ started = true;
+// consolePrintf("****Starting buffer*****\n");
+ memset(audioBuffer, 0, BUFFER_SIZE * 2);
+ memset(decompressionBuffer, 0, waveHeader.fmtExtra * 2);
+ DS::playSound(audioBuffer, BUFFER_SIZE * 2, false, false, waveHeader.fmtSamPerSec);
+
+ }
+ fillPos = (IPC->streamPlayingSection + 1) & 3;
+ isPlayingFlag = true;
+
+
+ // Startframe is a 75Hz timer. Dunno why, since nothing else
+ // seems to run at that rate.
+ int tenths = (startFrame * 10) / 75;
+
+ // Seek to the nearest block start to the start time
+ int samples = (tenths * waveHeader.fmtSamPerSec) / 10;
+ int block = samples / waveHeader.fmtExtra;
+
+
+ if (duration == 0) {
+ blocksLeft = 0;
+ } else {
+ blocksLeft = ((((duration * 100) / 75) * (waveHeader.fmtSamPerSec)) / (waveHeader.fmtExtra) / 100) + 10;
+ }
+// consolePrintf("Playing %d blocks (%d)\n\n", blocksLeft, duration);
+
+ // No need to seek if we're starting from the beginning
+ if (block != 0) {
+ DS::std_fseek(file, dataChunkStart + block * waveHeader.fmtBlockAlign, SEEK_SET);
+// consolePrintf("Startframe: %d msec: %d (%d,%d)\n", startFrame, tenthssec, samples, block);
+ }
+
+
+ //decompressBlock();
+ playNextBlock();
+ numLoops = numLoops;
+}
+
+void update() {
+ playNextBlock();
+}
+
+void decompressBlock() {
+ int block[2048];
+ bool loop = false;
+
+ blockCount++;
+
+ if (blockCount < 10) return;
+
+
+ do {
+ DS::std_fread((const void *) &blockHeader, sizeof(blockHeader), 1, file);
+
+ DS::std_fread(&block[0], waveHeader.fmtBlockAlign - sizeof(blockHeader), 1, file);
+
+ if (DS::std_feof(file) ) {
+ // Reached end of file, so loop
+
+
+ if ((numLoops == -1) || (numLoops > 1)) {
+ // Seek file to first packet
+ if (numLoops != -1) {
+ numLoops--;
+ }
+ DS::std_fseek(file, dataChunkStart, SEEK_SET);
+ loop = true;
+ } else {
+ // Fill decompression buffer with zeros to prevent glitching
+ for (int r = 0; r < waveHeader.fmtExtra; r++) {
+ decompressionBuffer[r] = 0;
+ }
+// consolePrintf("Stopping music\n");
+ stopTrack();
+ return;
+ }
+
+ } else {
+ loop = false;
+ }
+
+ } while (loop);
+
+
+ if (blocksLeft > 0) {
+ blocksLeft--;
+ // consolePrintf("%d ", blocksLeft);
+ if (blocksLeft == 0) {
+ stopTrack();
+ return;
+ }
+ }
+
+ // First sample is in header
+ decompressionBuffer[0] = blockHeader.firstSample;
+
+ // Set up initial table indeces
+ int stepTableIndex = blockHeader.stepTableIndex;
+ int prevSample = blockHeader.firstSample;
+
+// consolePrintf("Decompressing block step=%d fs=%d\n", stepTableIndex, prevSample);
+
+ for (int r = 0; r < waveHeader.fmtExtra - 1; r++) {
+
+ int word = block[r >> 3];
+ int offset = 0;
+
+ switch (7 - (r & 0x0007)) {
+ case 0: {
+ offset = (word & 0xF0000000) >> 28;
+ break;
+ }
+
+ case 1: {
+ offset = (word & 0x0F000000) >> 24;
+ break;
+ }
+
+ case 2: {
+ offset = (word & 0x00F00000) >> 20;
+ break;
+ }
+
+ case 3: {
+ offset = (word & 0x000F0000) >> 16;
+ break;
+ }
+
+ case 4: {
+ offset = (word & 0x0000F000) >> 12;
+ break;
+ }
+
+ case 5: {
+ offset = (word & 0x00000F00) >> 8;
+ break;
+ }
+
+ case 6: {
+ offset = (word & 0x000000F0) >> 4;
+ break;
+ }
+
+ case 7: {
+ offset = (word & 0x0000000F);
+ break;
+ }
+ }
+
+ int diff = 0;
+
+ if (offset & 4) {
+ diff = diff + stepTab[stepTableIndex];
+ }
+
+ if (offset & 2) {
+ diff = diff + (stepTab[stepTableIndex] >> 1);
+ }
+
+ if (offset & 1) {
+ diff = diff + (stepTab[stepTableIndex] >> 2);
+ }
+
+ diff = diff + (stepTab[stepTableIndex] >> 3);
+
+ if (offset & 8) {
+ diff = -diff;
+ }
+
+ int newSample = prevSample + diff;
+
+ if (newSample > 32767) newSample = 32767;
+ if (newSample < -32768) newSample = -32768;
+
+ decompressionBuffer[r + 1] = newSample;
+
+ prevSample = newSample;
+
+ stepTableIndex += indexTab[offset];
+
+ if (stepTableIndex > 88) stepTableIndex = 88;
+ if (stepTableIndex < 0) stepTableIndex = 0;
+
+
+ }
+}
+
+void playNextBlock() {
+ if (!isPlayingFlag) return;
+ int lastBlockId = -1;
+
+ while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed
+ IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7
+// DC_FlushAll();
+
+ //-8644, 25088
+ for (int block = fillPos + 1; block < fillPos + 4; block++) {
+
+ int blockId = block & 3;
+
+ if (IPC->streamFillNeeded[blockId]) {
+
+ IPC->streamFillNeeded[blockId] = false;
+// DC_FlushAll();
+
+/* if (!(REG_KEYINPUT & KEY_R)) {
+ //consolePrintf("Align: %d First: %d Step:%d Res:%d\n", waveHeader.fmtBlockAlign, blockHeader.firstSample, blockHeader.stepTableIndex, blockHeader.reserved);
+ consolePrintf("Filling buffer %d\n", blockId);
+ }*/
+ for (int r = blockId * BUFFER_CHUNK_SIZE; r < (blockId + 1) * BUFFER_CHUNK_SIZE; r++) {
+ if (isPlayingFlag) {
+ audioBuffer[r] = decompressionBuffer[sampleNum++];
+ if (sampleNum >= waveHeader.fmtExtra) {
+ decompressBlock();
+ sampleNum = 0;
+ }
+ }
+ }
+
+ lastBlockId = blockId;
+ IPC->streamFillNeeded[blockId] = false;
+// DC_FlushAll();
+
+ }
+
+
+
+ }
+
+
+
+ if (lastBlockId != -1) {
+ fillPos = lastBlockId;
+/* if (!(REG_KEYINPUT & KEY_R)) {
+ consolePrintf("Frame fill done\n");
+ }*/
+ }
+ IPC->adpcm.semaphore = false; // Release the buffer structure
+// DC_FlushAll();
+}
+
+void stopTrack() {
+ if (!isPlayingFlag) return;
+
+ DS::std_fclose(file);
+
+ isPlayingFlag = false;
+
+ for (int r = 0; r < BUFFER_SIZE; r++) {
+ audioBuffer[r] = 0;
+ }
+
+ for (int r= 0; r < waveHeader.fmtExtra; r++) {
+ decompressionBuffer[r] = 0;
+ }
+// DS::stopSound(1);
+
+// free(audioBuffer);
+// free(decompressionBuffer);
+
+ DC_FlushAll();
+}
+
+bool checkCD() {
+ // Need to check whethe CD audio files are present - do this by trying to open Track1.wav.
+ consolePrintf("Attempted to open cd drive\n");
+
+ Common::String path = ConfMan.get("path");
+ path = path + "/track2.wav";
+ // 6577 153 154
+ consolePrintf("Looking for %s...", path.c_str());
+
+ FILE* file;
+ if ((file = DS::std_fopen(path.c_str(), "r"))) {
+ consolePrintf("Success!\n");
+ setActive(true);
+ DS::std_fclose(file);
+ return true;
+ } else {
+ setActive(false);
+ consolePrintf("Failed!\n");
+ return false;
+ }
+}
+
+bool isPlaying() {
+ return isPlayingFlag;
+}
+
+}
+}
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "stdafx.h"
+#include "cdaudio.h"
+#include "ds-fs.h"
+#include "config-manager.h"
+#include "dsmain.h"
+#include "nds/scummvm_ipc.h"
+#include "console2.h"
+
+#define WAV_FORMAT_IMA_ADPCM 0x14
+#define BUFFER_SIZE 8192
+#define BUFFER_CHUNK_SIZE (BUFFER_SIZE >> 2)
+
+namespace DS {
+namespace CD {
+
+struct WaveHeader {
+
+ char riff[4]; // 'RIFF'
+ u32 size; // Size of the file
+ char wave[4]; // 'WAVE'
+
+ // fmt chunk
+ char fmt[4]; // 'fmt '
+ u32 fmtSize; // Chunk size
+ u16 fmtFormatTag; // Format of this file
+ u16 fmtChannels; // Num channels
+ u32 fmtSamPerSec; // Samples per second
+ u32 fmtBytesPerSec; // Bytes per second
+ u16 fmtBlockAlign; // Block alignment
+ u16 fmtBitsPerSam; // Bits per sample
+
+ u16 fmtExtraData; // Number of extra fmt bytes
+ u16 fmtExtra; // Samples per block (only for IMA-ADPCM files)
+} __attribute__ ((packed));
+
+struct chunkHeader {
+ char name[4];
+ u32 size;
+} __attribute__ ((packed));
+
+struct Header {
+ s16 firstSample;
+ char stepTableIndex;
+ char reserved;
+} __attribute__ ((packed));
+
+struct decoderFormat {
+ s16 initial;
+ unsigned char tableIndex;
+ unsigned char test;
+ unsigned char sample[1024];
+} __attribute__ ((packed));
+
+bool active = false;
+WaveHeader waveHeader;
+Header blockHeader;
+FILE* file;
+int fillPos;
+bool isPlayingFlag = false;
+
+s16* audioBuffer;
+u32 sampleNum;
+s16* decompressionBuffer;
+int numLoops;
+int blockCount;
+int dataChunkStart;
+int blocksLeft;
+
+
+// These are from Microsoft's document on DVI ADPCM
+const int stepTab[ 89 ] = {
+7, 8, 9, 10, 11, 12, 13, 14,
+16, 17, 19, 21, 23, 25, 28, 31,
+34, 37, 41, 45, 50, 55, 60, 66,
+73, 80, 88, 97, 107, 118, 130, 143,
+157, 173, 190, 209, 230, 253, 279, 307,
+337, 371, 408, 449, 494, 544, 598, 658,
+724, 796, 876, 963, 1060, 1166, 1282, 1411,
+1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+32767 };
+
+const int indexTab[ 16 ] = { -1, -1, -1, -1, 2, 4, 6, 8,
+-1, -1, -1, -1, 2, 4, 6, 8 };
+
+void playNextBlock();
+void decompressBlock();
+
+
+void allocBuffers() {
+
+}
+
+void setActive(bool active) {
+ active = active;
+}
+
+bool getActive() {
+ return active;
+}
+
+void playTrack(int track, int numLoops, int startFrame, int duration) {
+ Common::String path = ConfMan.get("path");
+
+ if (isPlayingFlag) {
+ stopTrack();
+ }
+
+
+
+
+
+ char str[100];
+ sprintf(str, "/track%d.wav", track);
+ path = path + str;
+
+
+ //1820160
+
+ file = DS::std_fopen(path.c_str(), "rb");
+
+ if (!file) {
+ consolePrintf("Failed to open %s!\n", path.c_str());
+ return;
+ }
+
+
+ DS::std_fread((const void *) &waveHeader, sizeof(waveHeader), 1, file);
+
+ consolePrintf("Playing track %d\n", track);
+ consolePrintf("Format: %d\n", waveHeader.fmtFormatTag);
+ consolePrintf("Rate : %d\n", waveHeader.fmtSamPerSec);
+ consolePrintf("Bits : %d\n", waveHeader.fmtBitsPerSam);
+ consolePrintf("BlkSz : %d\n", waveHeader.fmtExtra);
+
+ if ((waveHeader.fmtFormatTag != 17) && (waveHeader.fmtFormatTag != 20)) {
+ consolePrintf("Wave file is in the wrong format! You must use IMA-ADPCM 4-bit mono.\n");
+ return;
+ }
+
+ for (int r = 0; r < 8; r++) {
+ IPC->adpcm.buffer[r] = (u8 * volatile) (decoderFormat *) malloc(waveHeader.fmtBlockAlign);
+ IPC->adpcm.filled[r] = false;
+ IPC->adpcm.arm7Dirty[r] = false;
+ }
+
+ // Skip chunks until we reach the data chunk
+ chunkHeader chunk;
+ DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file);
+
+ while (!((chunk.name[0] == 'd') && (chunk.name[1] == 'a') && (chunk.name[2] == 't') && (chunk.name[3] == 'a'))) {
+ DS::std_fseek(file, chunk.size, SEEK_CUR);
+ DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file);
+ }
+
+ dataChunkStart = DS::std_ftell(file);
+
+
+ static bool started = false;
+ sampleNum = 0;
+ blockCount = 0;
+
+ IPC->streamFillNeeded[0] = true;
+ IPC->streamFillNeeded[1] = true;
+ IPC->streamFillNeeded[2] = true;
+ IPC->streamFillNeeded[3] = true;
+ if (!started) {
+ fillPos = 0;
+ audioBuffer = (s16 *) malloc(BUFFER_SIZE * 2);
+ decompressionBuffer = (s16 *) malloc(waveHeader.fmtExtra * 2);
+ started = true;
+// consolePrintf("****Starting buffer*****\n");
+ memset(audioBuffer, 0, BUFFER_SIZE * 2);
+ memset(decompressionBuffer, 0, waveHeader.fmtExtra * 2);
+ DS::playSound(audioBuffer, BUFFER_SIZE * 2, false, false, waveHeader.fmtSamPerSec);
+
+ }
+ fillPos = (IPC->streamPlayingSection + 1) & 3;
+ isPlayingFlag = true;
+
+
+ // Startframe is a 75Hz timer. Dunno why, since nothing else
+ // seems to run at that rate.
+ int tenths = (startFrame * 10) / 75;
+
+ // Seek to the nearest block start to the start time
+ int samples = (tenths * waveHeader.fmtSamPerSec) / 10;
+ int block = samples / waveHeader.fmtExtra;
+
+
+ if (duration == 0) {
+ blocksLeft = 0;
+ } else {
+ blocksLeft = ((((duration * 100) / 75) * (waveHeader.fmtSamPerSec)) / (waveHeader.fmtExtra) / 100) + 10;
+ }
+// consolePrintf("Playing %d blocks (%d)\n\n", blocksLeft, duration);
+
+ // No need to seek if we're starting from the beginning
+ if (block != 0) {
+ DS::std_fseek(file, dataChunkStart + block * waveHeader.fmtBlockAlign, SEEK_SET);
+// consolePrintf("Startframe: %d msec: %d (%d,%d)\n", startFrame, tenthssec, samples, block);
+ }
+
+
+ //decompressBlock();
+ playNextBlock();
+ numLoops = numLoops;
+}
+
+void update() {
+ playNextBlock();
+}
+
+void decompressBlock() {
+ int block[2048];
+ bool loop = false;
+
+ blockCount++;
+
+ if (blockCount < 10) return;
+
+
+ do {
+ DS::std_fread((const void *) &blockHeader, sizeof(blockHeader), 1, file);
+
+ DS::std_fread(&block[0], waveHeader.fmtBlockAlign - sizeof(blockHeader), 1, file);
+
+ if (DS::std_feof(file) ) {
+ // Reached end of file, so loop
+
+
+ if ((numLoops == -1) || (numLoops > 1)) {
+ // Seek file to first packet
+ if (numLoops != -1) {
+ numLoops--;
+ }
+ DS::std_fseek(file, dataChunkStart, SEEK_SET);
+ loop = true;
+ } else {
+ // Fill decompression buffer with zeros to prevent glitching
+ for (int r = 0; r < waveHeader.fmtExtra; r++) {
+ decompressionBuffer[r] = 0;
+ }
+// consolePrintf("Stopping music\n");
+ stopTrack();
+ return;
+ }
+
+ } else {
+ loop = false;
+ }
+
+ } while (loop);
+
+
+ if (blocksLeft > 0) {
+ blocksLeft--;
+ // consolePrintf("%d ", blocksLeft);
+ if (blocksLeft == 0) {
+ stopTrack();
+ return;
+ }
+ }
+
+ // First sample is in header
+ decompressionBuffer[0] = blockHeader.firstSample;
+
+ // Set up initial table indeces
+ int stepTableIndex = blockHeader.stepTableIndex;
+ int prevSample = blockHeader.firstSample;
+
+// consolePrintf("Decompressing block step=%d fs=%d\n", stepTableIndex, prevSample);
+
+ for (int r = 0; r < waveHeader.fmtExtra - 1; r++) {
+
+ int word = block[r >> 3];
+ int offset = 0;
+
+ switch (7 - (r & 0x0007)) {
+ case 0: {
+ offset = (word & 0xF0000000) >> 28;
+ break;
+ }
+
+ case 1: {
+ offset = (word & 0x0F000000) >> 24;
+ break;
+ }
+
+ case 2: {
+ offset = (word & 0x00F00000) >> 20;
+ break;
+ }
+
+ case 3: {
+ offset = (word & 0x000F0000) >> 16;
+ break;
+ }
+
+ case 4: {
+ offset = (word & 0x0000F000) >> 12;
+ break;
+ }
+
+ case 5: {
+ offset = (word & 0x00000F00) >> 8;
+ break;
+ }
+
+ case 6: {
+ offset = (word & 0x000000F0) >> 4;
+ break;
+ }
+
+ case 7: {
+ offset = (word & 0x0000000F);
+ break;
+ }
+ }
+
+ int diff = 0;
+
+ if (offset & 4) {
+ diff = diff + stepTab[stepTableIndex];
+ }
+
+ if (offset & 2) {
+ diff = diff + (stepTab[stepTableIndex] >> 1);
+ }
+
+ if (offset & 1) {
+ diff = diff + (stepTab[stepTableIndex] >> 2);
+ }
+
+ diff = diff + (stepTab[stepTableIndex] >> 3);
+
+ if (offset & 8) {
+ diff = -diff;
+ }
+
+ int newSample = prevSample + diff;
+
+ if (newSample > 32767) newSample = 32767;
+ if (newSample < -32768) newSample = -32768;
+
+ decompressionBuffer[r + 1] = newSample;
+
+ prevSample = newSample;
+
+ stepTableIndex += indexTab[offset];
+
+ if (stepTableIndex > 88) stepTableIndex = 88;
+ if (stepTableIndex < 0) stepTableIndex = 0;
+
+
+ }
+}
+
+void playNextBlock() {
+ if (!isPlayingFlag) return;
+ int lastBlockId = -1;
+
+ while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed
+ IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7
+// DC_FlushAll();
+
+ //-8644, 25088
+ for (int block = fillPos + 1; block < fillPos + 4; block++) {
+
+ int blockId = block & 3;
+
+ if (IPC->streamFillNeeded[blockId]) {
+
+ IPC->streamFillNeeded[blockId] = false;
+// DC_FlushAll();
+
+/* if (!(REG_KEYINPUT & KEY_R)) {
+ //consolePrintf("Align: %d First: %d Step:%d Res:%d\n", waveHeader.fmtBlockAlign, blockHeader.firstSample, blockHeader.stepTableIndex, blockHeader.reserved);
+ consolePrintf("Filling buffer %d\n", blockId);
+ }*/
+ for (int r = blockId * BUFFER_CHUNK_SIZE; r < (blockId + 1) * BUFFER_CHUNK_SIZE; r++) {
+ if (isPlayingFlag) {
+ audioBuffer[r] = decompressionBuffer[sampleNum++];
+ if (sampleNum >= waveHeader.fmtExtra) {
+ decompressBlock();
+ sampleNum = 0;
+ }
+ }
+ }
+
+ lastBlockId = blockId;
+ IPC->streamFillNeeded[blockId] = false;
+// DC_FlushAll();
+
+ }
+
+
+
+ }
+
+
+
+ if (lastBlockId != -1) {
+ fillPos = lastBlockId;
+/* if (!(REG_KEYINPUT & KEY_R)) {
+ consolePrintf("Frame fill done\n");
+ }*/
+ }
+ IPC->adpcm.semaphore = false; // Release the buffer structure
+// DC_FlushAll();
+}
+
+void stopTrack() {
+ if (!isPlayingFlag) return;
+
+ DS::std_fclose(file);
+
+ isPlayingFlag = false;
+
+ for (int r = 0; r < BUFFER_SIZE; r++) {
+ audioBuffer[r] = 0;
+ }
+
+ for (int r= 0; r < waveHeader.fmtExtra; r++) {
+ decompressionBuffer[r] = 0;
+ }
+// DS::stopSound(1);
+
+// free(audioBuffer);
+// free(decompressionBuffer);
+
+ DC_FlushAll();
+}
+
+bool checkCD() {
+ // Need to check whethe CD audio files are present - do this by trying to open Track1.wav.
+ consolePrintf("Attempted to open cd drive\n");
+
+ Common::String path = ConfMan.get("path");
+ path = path + "/track2.wav";
+ // 6577 153 154
+ consolePrintf("Looking for %s...", path.c_str());
+
+ FILE* file;
+ if ((file = DS::std_fopen(path.c_str(), "r"))) {
+ consolePrintf("Success!\n");
+ setActive(true);
+ DS::std_fclose(file);
+ return true;
+ } else {
+ setActive(false);
+ consolePrintf("Failed!\n");
+ return false;
+ }
+}
+
+bool isPlaying() {
+ return isPlayingFlag;
+}
+
+}
+}
+
diff --git a/backends/platform/ds/arm9/source/cdaudio.h b/backends/platform/ds/arm9/source/cdaudio.h
new file mode 100644
index 0000000000..8abee25e77
--- /dev/null
+++ b/backends/platform/ds/arm9/source/cdaudio.h
@@ -0,0 +1,74 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+ #ifndef _CDAUDIO_H_
+#define _CDAUDIO_H_
+
+namespace DS {
+namespace CD {
+
+void setActive(bool active);
+void playTrack(int track, int numLoops, int startFrame, int duration);
+void stopTrack();
+bool checkCD();
+bool getActive();
+bool isPlaying();
+void update();
+
+}
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+ #ifndef _CDAUDIO_H_
+#define _CDAUDIO_H_
+
+namespace DS {
+namespace CD {
+
+void setActive(bool active);
+void playTrack(int track, int numLoops, int startFrame, int duration);
+void stopTrack();
+bool checkCD();
+bool getActive();
+bool isPlaying();
+void update();
+
+}
+}
+
+#endif
diff --git a/backends/platform/ds/arm9/source/compressor/lz.cpp b/backends/platform/ds/arm9/source/compressor/lz.cpp
new file mode 100644
index 0000000000..237b432ce8
--- /dev/null
+++ b/backends/platform/ds/arm9/source/compressor/lz.cpp
@@ -0,0 +1,1078 @@
+/*************************************************************************
+* Name: lz.c
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder implementation.
+* Reentrant: Yes
+* $Id$
+*
+* The LZ77 compression scheme is a substitutional compression scheme
+* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in
+* its design, and uses no fancy bit level compression.
+*
+* This is my first attempt at an implementation of a LZ77 code/decoder.
+*
+* The principle of the LZ77 compression algorithm is to store repeated
+* occurrences of strings as references to previous occurrences of the same
+* string. The point is that the reference consumes less space than the
+* string itself, provided that the string is long enough (in this
+* implementation, the string has to be at least 4 bytes long, since the
+* minimum coded reference is 3 bytes long). Also note that the term
+* "string" refers to any kind of byte sequence (it does not have to be
+* an ASCII string, for instance).
+*
+* The coder uses a brute force approach to finding string matches in the
+* history buffer (or "sliding window", if you wish), which is very, very
+* slow. I recon the complexity is somewhere between O(n^2) and O(n^3),
+* depending on the input data.
+*
+* There is also a faster implementation that uses a large working buffer
+* in which a "jump table" is stored, which is used to quickly find
+* possible string matches (see the source code for LZ_CompressFast() for
+* more information). The faster method is an order of magnitude faster,
+* and also does a full string search in the entire input buffer (it does
+* not use a sliding window).
+*
+* The upside is that decompression is very fast, and the compression ratio
+* is often very good.
+*
+* The reference to a string is coded as a (length,offset) pair, where the
+* length indicates the length of the string, and the offset gives the
+* offset from the current data position. To distinguish between string
+* references and literal strings (uncompressed bytes), a string reference
+* is preceded by a marker byte, which is chosen as the least common byte
+* symbol in the input data stream (this marker byte is stored in the
+* output stream as the first byte).
+*
+* Occurrences of the marker byte in the stream are encoded as the marker
+* byte followed by a zero byte, which means that occurrences of the marker
+* byte have to be coded with two bytes.
+*
+* The lengths and offsets are coded in a variable length fashion, allowing
+* values of any magnitude (up to 4294967295 in this implementation).
+*
+* With this compression scheme, the worst case compression result is
+* (257/256)*insize + 1.
+*
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2004 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+
+/*************************************************************************
+* Constants used for LZ77 coding
+*************************************************************************/
+
+/* Maximum offset (can be any size < 2^32). Lower values gives faster
+ compression, while higher values gives better compression.
+ NOTE: LZ_CompressFast does not use this constant. */
+#define LZ_MAX_OFFSET 512
+
+
+
+/*************************************************************************
+* INTERNAL FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* _LZ_StringCompare() - Return maximum length string match.
+*************************************************************************/
+
+inline static unsigned int _LZ_StringCompare( unsigned char * str1,
+ unsigned char * str2, unsigned int minlen, unsigned int maxlen )
+{
+ unsigned int len;
+
+ for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len );
+
+ return len;
+}
+
+
+/*************************************************************************
+* _LZ_WriteVarSize() - Write unsigned integer with variable number of
+* bytes depending on value.
+*************************************************************************/
+
+inline static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf )
+{
+ unsigned int y;
+ int num_bytes, i, b;
+
+ /* Determine number of bytes needed to store the number x */
+ y = x >> 3;
+ for( num_bytes = 5; num_bytes >= 2; -- num_bytes )
+ {
+ if( y & 0xfe000000 ) break;
+ y <<= 7;
+ }
+
+ /* Write all bytes, seven bits in each, with 8:th bit set for all */
+ /* but the last byte. */
+ for( i = num_bytes-1; i >= 0; -- i )
+ {
+ b = (x >> (i*7)) & 0x0000007f;
+ if( i > 0 )
+ {
+ b |= 0x00000080;
+ }
+ *buf ++ = (unsigned char) b;
+ }
+
+ /* Return number of bytes written */
+ return num_bytes;
+}
+
+
+/*************************************************************************
+* _LZ_ReadVarSize() - Read unsigned integer with variable number of
+* bytes depending on value.
+*************************************************************************/
+
+inline static int _LZ_ReadVarSize( unsigned int * x, unsigned char * buf )
+{
+ unsigned int y, b, num_bytes;
+
+ /* Read complete value (stop when byte contains zero in 8:th bit) */
+ y = 0;
+ num_bytes = 0;
+ do
+ {
+ b = (unsigned int) (*buf ++);
+ y = (y << 7) | (b & 0x0000007f);
+ ++ num_bytes;
+ }
+ while( b & 0x00000080 );
+
+ /* Store value in x */
+ *x = y;
+
+ /* Return number of bytes read */
+ return num_bytes;
+}
+
+
+
+/*************************************************************************
+* PUBLIC FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* LZ_Compress() - Compress a block of data using an LZ77 coder.
+* in - Input (uncompressed) buffer.
+* out - Output (compressed) buffer. This buffer must be 0.4% larger
+* than the input buffer, plus one byte.
+* insize - Number of input bytes.
+* The function returns the size of the compressed data.
+*************************************************************************/
+
+int LZ_Compress( unsigned char *in, unsigned char *out,
+ unsigned int insize )
+{
+ unsigned char marker, symbol;
+ unsigned int inpos, outpos, bytesleft, i;
+ unsigned int maxoffset, offset, bestoffset;
+ unsigned int maxlength, length, bestlength;
+ unsigned int histogram[ 256 ];
+ unsigned char *ptr1, *ptr2;
+
+ /* Do we have anything to compress? */
+ if( insize < 1 )
+ {
+ return 0;
+ }
+
+ /* Create histogram */
+ for( i = 0; i < 256; ++ i )
+ {
+ histogram[ i ] = 0;
+ }
+ for( i = 0; i < insize; ++ i )
+ {
+ ++ histogram[ in[ i ] ];
+ }
+
+ /* Find the least common byte, and use it as the code marker */
+ marker = 0;
+ for( i = 1; i < 256; ++ i )
+ {
+ if( histogram[ i ] < histogram[ marker ] )
+ {
+ marker = i;
+ }
+ }
+
+ /* Remember the repetition marker for the decoder */
+ out[ 0 ] = marker;
+
+ /* Start of compression */
+ inpos = 0;
+ outpos = 1;
+
+ /* Main compression loop */
+ bytesleft = insize;
+ do
+ {
+ /* Determine most distant position */
+ if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET;
+ else maxoffset = inpos;
+
+ /* Get pointer to current position */
+ ptr1 = &in[ inpos ];
+
+ /* Search history window for maximum length string match */
+ bestlength = 3;
+ bestoffset = 0;
+ for( offset = 3; offset <= maxoffset; ++ offset )
+ {
+ /* Get pointer to candidate string */
+ ptr2 = &ptr1[ -offset ];
+
+ /* Quickly determine if this is a candidate (for speed) */
+ if( (ptr1[ 0 ] == ptr2[ 0 ]) &&
+ (ptr1[ bestlength ] == ptr2[ bestlength ]) )
+ {
+ /* Determine maximum length for this offset */
+ maxlength = (bytesleft < offset ? bytesleft : offset);
+
+ /* Count maximum length match at this offset */
+ length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength );
+
+ /* Better match than any previous match? */
+ if( length > bestlength )
+ {
+ bestlength = length;
+ bestoffset = offset;
+ }
+ }
+ }
+
+ /* Was there a good enough match? */
+ if( (bestlength >= 8) ||
+ ((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
+ ((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
+ ((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
+ ((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
+ {
+ out[ outpos ++ ] = (unsigned char) marker;
+ outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
+ outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
+ inpos += bestlength;
+ bytesleft -= bestlength;
+ }
+ else
+ {
+ /* Output single byte (or two bytes if marker byte) */
+ symbol = in[ inpos ++ ];
+ out[ outpos ++ ] = symbol;
+ if( symbol == marker )
+ {
+ out[ outpos ++ ] = 0;
+ }
+ -- bytesleft;
+ }
+ }
+ while( bytesleft > 3 );
+
+ /* Dump remaining bytes, if any */
+ while( inpos < insize )
+ {
+ if( in[ inpos ] == marker )
+ {
+ out[ outpos ++ ] = marker;
+ out[ outpos ++ ] = 0;
+ }
+ else
+ {
+ out[ outpos ++ ] = in[ inpos ];
+ }
+ ++ inpos;
+ }
+
+ return outpos;
+}
+
+
+/*************************************************************************
+* LZ_CompressFast() - Compress a block of data using an LZ77 coder.
+* in - Input (uncompressed) buffer.
+* out - Output (compressed) buffer. This buffer must be 0.4% larger
+* than the input buffer, plus one byte.
+* insize - Number of input bytes.
+* work - Pointer to a temporary buffer (internal working buffer), which
+* must be able to hold (insize+65536) unsigned integers.
+* The function returns the size of the compressed data.
+*************************************************************************/
+
+int LZ_CompressFast( unsigned char *in, unsigned char *out,
+ unsigned int insize, unsigned int *work )
+{
+ unsigned char marker, symbol;
+ unsigned int inpos, outpos, bytesleft, i, index, symbols;
+ unsigned int offset, bestoffset;
+ unsigned int maxlength, length, bestlength;
+ unsigned int histogram[ 256 ], *lastindex, *jumptable;
+ unsigned char *ptr1, *ptr2;
+
+ /* Do we have anything to compress? */
+ if( insize < 1 )
+ {
+ return 0;
+ }
+
+ /* Assign arrays to the working area */
+ lastindex = work;
+ jumptable = &work[ 65536 ];
+
+ /* Build a "jump table". Here is how the jump table works:
+ jumptable[i] points to the nearest previous occurrence of the same
+ symbol pair as in[i]:in[i+1], so in[i] == in[jumptable[i]] and
+ in[i+1] == in[jumptable[i]+1]. Following the jump table gives a
+ dramatic boost for the string search'n'match loop compared to doing
+ a brute force search. */
+ for( i = 0; i < 65536; ++ i )
+ {
+ lastindex[ i ] = 0xffffffff;
+ }
+ for( i = 0; i < insize-1; ++ i )
+ {
+ symbols = (((unsigned int)in[i]) << 8) | ((unsigned int)in[i+1]);
+ index = lastindex[ symbols ];
+ lastindex[ symbols ] = i;
+ jumptable[ i ] = index;
+ }
+ jumptable[ insize-1 ] = 0xffffffff;
+
+ /* Create histogram */
+ for( i = 0; i < 256; ++ i )
+ {
+ histogram[ i ] = 0;
+ }
+ for( i = 0; i < insize; ++ i )
+ {
+ ++ histogram[ in[ i ] ];
+ }
+
+ /* Find the least common byte, and use it as the code marker */
+ marker = 0;
+ for( i = 1; i < 256; ++ i )
+ {
+ if( histogram[ i ] < histogram[ marker ] )
+ {
+ marker = i;
+ }
+ }
+
+ /* Remember the repetition marker for the decoder */
+ out[ 0 ] = marker;
+
+ /* Start of compression */
+ inpos = 0;
+ outpos = 1;
+
+ /* Main compression loop */
+ bytesleft = insize;
+ do
+ {
+ /* Get pointer to current position */
+ ptr1 = &in[ inpos ];
+
+ /* Search history window for maximum length string match */
+ bestlength = 3;
+ bestoffset = 0;
+ index = jumptable[ inpos ];
+ while( index != 0xffffffff )
+ {
+ /* Get pointer to candidate string */
+ ptr2 = &in[ index ];
+
+ /* Quickly determine if this is a candidate (for speed) */
+ if( ptr2[ bestlength ] == ptr1[ bestlength ] )
+ {
+ /* Determine maximum length for this offset */
+ offset = inpos - index;
+ maxlength = (bytesleft < offset ? bytesleft : offset);
+
+ /* Count maximum length match at this offset */
+ length = _LZ_StringCompare( ptr1, ptr2, 2, maxlength );
+
+ /* Better match than any previous match? */
+ if( length > bestlength )
+ {
+ bestlength = length;
+ bestoffset = offset;
+ }
+ }
+
+ /* Get next possible index from jump table */
+ index = jumptable[ index ];
+ }
+
+ /* Was there a good enough match? */
+ if( (bestlength >= 8) ||
+ ((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
+ ((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
+ ((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
+ ((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
+ {
+ out[ outpos ++ ] = (unsigned char) marker;
+ outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
+ outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
+ inpos += bestlength;
+ bytesleft -= bestlength;
+ }
+ else
+ {
+ /* Output single byte (or two bytes if marker byte) */
+ symbol = in[ inpos ++ ];
+ out[ outpos ++ ] = symbol;
+ if( symbol == marker )
+ {
+ out[ outpos ++ ] = 0;
+ }
+ -- bytesleft;
+ }
+ }
+ while( bytesleft > 3 );
+
+ /* Dump remaining bytes, if any */
+ while( inpos < insize )
+ {
+ if( in[ inpos ] == marker )
+ {
+ out[ outpos ++ ] = marker;
+ out[ outpos ++ ] = 0;
+ }
+ else
+ {
+ out[ outpos ++ ] = in[ inpos ];
+ }
+ ++ inpos;
+ }
+
+ return outpos;
+}
+
+
+/*************************************************************************
+* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
+* in - Input (compressed) buffer.
+* out - Output (uncompressed) buffer. This buffer must be large
+* enough to hold the uncompressed data.
+* insize - Number of input bytes.
+*************************************************************************/
+
+void LZ_Uncompress( unsigned char *in, unsigned char *out,
+ unsigned int insize )
+{
+ unsigned char marker, symbol;
+ unsigned int i, inpos, outpos, length, offset;
+
+ /* Do we have anything to compress? */
+ if( insize < 1 )
+ {
+ return;
+ }
+
+ /* Get marker symbol from input stream */
+ marker = in[ 0 ];
+ inpos = 1;
+
+ /* Main decompression loop */
+ outpos = 0;
+ do
+ {
+ symbol = in[ inpos ++ ];
+ if( symbol == marker )
+ {
+ /* We had a marker byte */
+ if( in[ inpos ] == 0 )
+ {
+ /* It was a single occurrence of the marker byte */
+ out[ outpos ++ ] = marker;
+ ++ inpos;
+ }
+ else
+ {
+ /* Extract true length and offset */
+ inpos += _LZ_ReadVarSize( &length, &in[ inpos ] );
+ inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] );
+
+ /* Copy corresponding data from history window */
+ for( i = 0; i < length; ++ i )
+ {
+ out[ outpos ] = out[ outpos - offset ];
+ ++ outpos;
+ }
+ }
+ }
+ else
+ {
+ /* No marker, plain copy */
+ out[ outpos ++ ] = symbol;
+ }
+ }
+ while( inpos < insize );
+}
+/*************************************************************************
+* Name: lz.c
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder implementation.
+* Reentrant: Yes
+* $Id$
+*
+* The LZ77 compression scheme is a substitutional compression scheme
+* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in
+* its design, and uses no fancy bit level compression.
+*
+* This is my first attempt at an implementation of a LZ77 code/decoder.
+*
+* The principle of the LZ77 compression algorithm is to store repeated
+* occurrences of strings as references to previous occurrences of the same
+* string. The point is that the reference consumes less space than the
+* string itself, provided that the string is long enough (in this
+* implementation, the string has to be at least 4 bytes long, since the
+* minimum coded reference is 3 bytes long). Also note that the term
+* "string" refers to any kind of byte sequence (it does not have to be
+* an ASCII string, for instance).
+*
+* The coder uses a brute force approach to finding string matches in the
+* history buffer (or "sliding window", if you wish), which is very, very
+* slow. I recon the complexity is somewhere between O(n^2) and O(n^3),
+* depending on the input data.
+*
+* There is also a faster implementation that uses a large working buffer
+* in which a "jump table" is stored, which is used to quickly find
+* possible string matches (see the source code for LZ_CompressFast() for
+* more information). The faster method is an order of magnitude faster,
+* and also does a full string search in the entire input buffer (it does
+* not use a sliding window).
+*
+* The upside is that decompression is very fast, and the compression ratio
+* is often very good.
+*
+* The reference to a string is coded as a (length,offset) pair, where the
+* length indicates the length of the string, and the offset gives the
+* offset from the current data position. To distinguish between string
+* references and literal strings (uncompressed bytes), a string reference
+* is preceded by a marker byte, which is chosen as the least common byte
+* symbol in the input data stream (this marker byte is stored in the
+* output stream as the first byte).
+*
+* Occurrences of the marker byte in the stream are encoded as the marker
+* byte followed by a zero byte, which means that occurrences of the marker
+* byte have to be coded with two bytes.
+*
+* The lengths and offsets are coded in a variable length fashion, allowing
+* values of any magnitude (up to 4294967295 in this implementation).
+*
+* With this compression scheme, the worst case compression result is
+* (257/256)*insize + 1.
+*
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2004 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+
+/*************************************************************************
+* Constants used for LZ77 coding
+*************************************************************************/
+
+/* Maximum offset (can be any size < 2^32). Lower values gives faster
+ compression, while higher values gives better compression.
+ NOTE: LZ_CompressFast does not use this constant. */
+#define LZ_MAX_OFFSET 512
+
+
+
+/*************************************************************************
+* INTERNAL FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* _LZ_StringCompare() - Return maximum length string match.
+*************************************************************************/
+
+inline static unsigned int _LZ_StringCompare( unsigned char * str1,
+ unsigned char * str2, unsigned int minlen, unsigned int maxlen )
+{
+ unsigned int len;
+
+ for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len );
+
+ return len;
+}
+
+
+/*************************************************************************
+* _LZ_WriteVarSize() - Write unsigned integer with variable number of
+* bytes depending on value.
+*************************************************************************/
+
+inline static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf )
+{
+ unsigned int y;
+ int num_bytes, i, b;
+
+ /* Determine number of bytes needed to store the number x */
+ y = x >> 3;
+ for( num_bytes = 5; num_bytes >= 2; -- num_bytes )
+ {
+ if( y & 0xfe000000 ) break;
+ y <<= 7;
+ }
+
+ /* Write all bytes, seven bits in each, with 8:th bit set for all */
+ /* but the last byte. */
+ for( i = num_bytes-1; i >= 0; -- i )
+ {
+ b = (x >> (i*7)) & 0x0000007f;
+ if( i > 0 )
+ {
+ b |= 0x00000080;
+ }
+ *buf ++ = (unsigned char) b;
+ }
+
+ /* Return number of bytes written */
+ return num_bytes;
+}
+
+
+/*************************************************************************
+* _LZ_ReadVarSize() - Read unsigned integer with variable number of
+* bytes depending on value.
+*************************************************************************/
+
+inline static int _LZ_ReadVarSize( unsigned int * x, unsigned char * buf )
+{
+ unsigned int y, b, num_bytes;
+
+ /* Read complete value (stop when byte contains zero in 8:th bit) */
+ y = 0;
+ num_bytes = 0;
+ do
+ {
+ b = (unsigned int) (*buf ++);
+ y = (y << 7) | (b & 0x0000007f);
+ ++ num_bytes;
+ }
+ while( b & 0x00000080 );
+
+ /* Store value in x */
+ *x = y;
+
+ /* Return number of bytes read */
+ return num_bytes;
+}
+
+
+
+/*************************************************************************
+* PUBLIC FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* LZ_Compress() - Compress a block of data using an LZ77 coder.
+* in - Input (uncompressed) buffer.
+* out - Output (compressed) buffer. This buffer must be 0.4% larger
+* than the input buffer, plus one byte.
+* insize - Number of input bytes.
+* The function returns the size of the compressed data.
+*************************************************************************/
+
+int LZ_Compress( unsigned char *in, unsigned char *out,
+ unsigned int insize )
+{
+ unsigned char marker, symbol;
+ unsigned int inpos, outpos, bytesleft, i;
+ unsigned int maxoffset, offset, bestoffset;
+ unsigned int maxlength, length, bestlength;
+ unsigned int histogram[ 256 ];
+ unsigned char *ptr1, *ptr2;
+
+ /* Do we have anything to compress? */
+ if( insize < 1 )
+ {
+ return 0;
+ }
+
+ /* Create histogram */
+ for( i = 0; i < 256; ++ i )
+ {
+ histogram[ i ] = 0;
+ }
+ for( i = 0; i < insize; ++ i )
+ {
+ ++ histogram[ in[ i ] ];
+ }
+
+ /* Find the least common byte, and use it as the code marker */
+ marker = 0;
+ for( i = 1; i < 256; ++ i )
+ {
+ if( histogram[ i ] < histogram[ marker ] )
+ {
+ marker = i;
+ }
+ }
+
+ /* Remember the repetition marker for the decoder */
+ out[ 0 ] = marker;
+
+ /* Start of compression */
+ inpos = 0;
+ outpos = 1;
+
+ /* Main compression loop */
+ bytesleft = insize;
+ do
+ {
+ /* Determine most distant position */
+ if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET;
+ else maxoffset = inpos;
+
+ /* Get pointer to current position */
+ ptr1 = &in[ inpos ];
+
+ /* Search history window for maximum length string match */
+ bestlength = 3;
+ bestoffset = 0;
+ for( offset = 3; offset <= maxoffset; ++ offset )
+ {
+ /* Get pointer to candidate string */
+ ptr2 = &ptr1[ -offset ];
+
+ /* Quickly determine if this is a candidate (for speed) */
+ if( (ptr1[ 0 ] == ptr2[ 0 ]) &&
+ (ptr1[ bestlength ] == ptr2[ bestlength ]) )
+ {
+ /* Determine maximum length for this offset */
+ maxlength = (bytesleft < offset ? bytesleft : offset);
+
+ /* Count maximum length match at this offset */
+ length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength );
+
+ /* Better match than any previous match? */
+ if( length > bestlength )
+ {
+ bestlength = length;
+ bestoffset = offset;
+ }
+ }
+ }
+
+ /* Was there a good enough match? */
+ if( (bestlength >= 8) ||
+ ((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
+ ((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
+ ((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
+ ((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
+ {
+ out[ outpos ++ ] = (unsigned char) marker;
+ outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
+ outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
+ inpos += bestlength;
+ bytesleft -= bestlength;
+ }
+ else
+ {
+ /* Output single byte (or two bytes if marker byte) */
+ symbol = in[ inpos ++ ];
+ out[ outpos ++ ] = symbol;
+ if( symbol == marker )
+ {
+ out[ outpos ++ ] = 0;
+ }
+ -- bytesleft;
+ }
+ }
+ while( bytesleft > 3 );
+
+ /* Dump remaining bytes, if any */
+ while( inpos < insize )
+ {
+ if( in[ inpos ] == marker )
+ {
+ out[ outpos ++ ] = marker;
+ out[ outpos ++ ] = 0;
+ }
+ else
+ {
+ out[ outpos ++ ] = in[ inpos ];
+ }
+ ++ inpos;
+ }
+
+ return outpos;
+}
+
+
+/*************************************************************************
+* LZ_CompressFast() - Compress a block of data using an LZ77 coder.
+* in - Input (uncompressed) buffer.
+* out - Output (compressed) buffer. This buffer must be 0.4% larger
+* than the input buffer, plus one byte.
+* insize - Number of input bytes.
+* work - Pointer to a temporary buffer (internal working buffer), which
+* must be able to hold (insize+65536) unsigned integers.
+* The function returns the size of the compressed data.
+*************************************************************************/
+
+int LZ_CompressFast( unsigned char *in, unsigned char *out,
+ unsigned int insize, unsigned int *work )
+{
+ unsigned char marker, symbol;
+ unsigned int inpos, outpos, bytesleft, i, index, symbols;
+ unsigned int offset, bestoffset;
+ unsigned int maxlength, length, bestlength;
+ unsigned int histogram[ 256 ], *lastindex, *jumptable;
+ unsigned char *ptr1, *ptr2;
+
+ /* Do we have anything to compress? */
+ if( insize < 1 )
+ {
+ return 0;
+ }
+
+ /* Assign arrays to the working area */
+ lastindex = work;
+ jumptable = &work[ 65536 ];
+
+ /* Build a "jump table". Here is how the jump table works:
+ jumptable[i] points to the nearest previous occurrence of the same
+ symbol pair as in[i]:in[i+1], so in[i] == in[jumptable[i]] and
+ in[i+1] == in[jumptable[i]+1]. Following the jump table gives a
+ dramatic boost for the string search'n'match loop compared to doing
+ a brute force search. */
+ for( i = 0; i < 65536; ++ i )
+ {
+ lastindex[ i ] = 0xffffffff;
+ }
+ for( i = 0; i < insize-1; ++ i )
+ {
+ symbols = (((unsigned int)in[i]) << 8) | ((unsigned int)in[i+1]);
+ index = lastindex[ symbols ];
+ lastindex[ symbols ] = i;
+ jumptable[ i ] = index;
+ }
+ jumptable[ insize-1 ] = 0xffffffff;
+
+ /* Create histogram */
+ for( i = 0; i < 256; ++ i )
+ {
+ histogram[ i ] = 0;
+ }
+ for( i = 0; i < insize; ++ i )
+ {
+ ++ histogram[ in[ i ] ];
+ }
+
+ /* Find the least common byte, and use it as the code marker */
+ marker = 0;
+ for( i = 1; i < 256; ++ i )
+ {
+ if( histogram[ i ] < histogram[ marker ] )
+ {
+ marker = i;
+ }
+ }
+
+ /* Remember the repetition marker for the decoder */
+ out[ 0 ] = marker;
+
+ /* Start of compression */
+ inpos = 0;
+ outpos = 1;
+
+ /* Main compression loop */
+ bytesleft = insize;
+ do
+ {
+ /* Get pointer to current position */
+ ptr1 = &in[ inpos ];
+
+ /* Search history window for maximum length string match */
+ bestlength = 3;
+ bestoffset = 0;
+ index = jumptable[ inpos ];
+ while( index != 0xffffffff )
+ {
+ /* Get pointer to candidate string */
+ ptr2 = &in[ index ];
+
+ /* Quickly determine if this is a candidate (for speed) */
+ if( ptr2[ bestlength ] == ptr1[ bestlength ] )
+ {
+ /* Determine maximum length for this offset */
+ offset = inpos - index;
+ maxlength = (bytesleft < offset ? bytesleft : offset);
+
+ /* Count maximum length match at this offset */
+ length = _LZ_StringCompare( ptr1, ptr2, 2, maxlength );
+
+ /* Better match than any previous match? */
+ if( length > bestlength )
+ {
+ bestlength = length;
+ bestoffset = offset;
+ }
+ }
+
+ /* Get next possible index from jump table */
+ index = jumptable[ index ];
+ }
+
+ /* Was there a good enough match? */
+ if( (bestlength >= 8) ||
+ ((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
+ ((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
+ ((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
+ ((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
+ {
+ out[ outpos ++ ] = (unsigned char) marker;
+ outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
+ outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
+ inpos += bestlength;
+ bytesleft -= bestlength;
+ }
+ else
+ {
+ /* Output single byte (or two bytes if marker byte) */
+ symbol = in[ inpos ++ ];
+ out[ outpos ++ ] = symbol;
+ if( symbol == marker )
+ {
+ out[ outpos ++ ] = 0;
+ }
+ -- bytesleft;
+ }
+ }
+ while( bytesleft > 3 );
+
+ /* Dump remaining bytes, if any */
+ while( inpos < insize )
+ {
+ if( in[ inpos ] == marker )
+ {
+ out[ outpos ++ ] = marker;
+ out[ outpos ++ ] = 0;
+ }
+ else
+ {
+ out[ outpos ++ ] = in[ inpos ];
+ }
+ ++ inpos;
+ }
+
+ return outpos;
+}
+
+
+/*************************************************************************
+* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
+* in - Input (compressed) buffer.
+* out - Output (uncompressed) buffer. This buffer must be large
+* enough to hold the uncompressed data.
+* insize - Number of input bytes.
+*************************************************************************/
+
+void LZ_Uncompress( unsigned char *in, unsigned char *out,
+ unsigned int insize )
+{
+ unsigned char marker, symbol;
+ unsigned int i, inpos, outpos, length, offset;
+
+ /* Do we have anything to compress? */
+ if( insize < 1 )
+ {
+ return;
+ }
+
+ /* Get marker symbol from input stream */
+ marker = in[ 0 ];
+ inpos = 1;
+
+ /* Main decompression loop */
+ outpos = 0;
+ do
+ {
+ symbol = in[ inpos ++ ];
+ if( symbol == marker )
+ {
+ /* We had a marker byte */
+ if( in[ inpos ] == 0 )
+ {
+ /* It was a single occurrence of the marker byte */
+ out[ outpos ++ ] = marker;
+ ++ inpos;
+ }
+ else
+ {
+ /* Extract true length and offset */
+ inpos += _LZ_ReadVarSize( &length, &in[ inpos ] );
+ inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] );
+
+ /* Copy corresponding data from history window */
+ for( i = 0; i < length; ++ i )
+ {
+ out[ outpos ] = out[ outpos - offset ];
+ ++ outpos;
+ }
+ }
+ }
+ else
+ {
+ /* No marker, plain copy */
+ out[ outpos ++ ] = symbol;
+ }
+ }
+ while( inpos < insize );
+}
diff --git a/backends/platform/ds/arm9/source/compressor/lz.h b/backends/platform/ds/arm9/source/compressor/lz.h
new file mode 100644
index 0000000000..dc10210742
--- /dev/null
+++ b/backends/platform/ds/arm9/source/compressor/lz.h
@@ -0,0 +1,100 @@
+/*************************************************************************
+* Name: lz.h
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder interface.
+* Reentrant: Yes
+* $Id$
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2004 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+#ifndef _lz_h_
+#define _lz_h_
+
+
+
+/*************************************************************************
+* Function prototypes
+*************************************************************************/
+
+int LZ_Compress( unsigned char *in, unsigned char *out,
+ unsigned int insize );
+int LZ_CompressFast( unsigned char *in, unsigned char *out,
+ unsigned int insize, unsigned int *work );
+void LZ_Uncompress( unsigned char *in, unsigned char *out,
+ unsigned int insize );
+
+
+#endif /* _lz_h_ */
+/*************************************************************************
+* Name: lz.h
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder interface.
+* Reentrant: Yes
+* $Id$
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2004 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+#ifndef _lz_h_
+#define _lz_h_
+
+
+
+/*************************************************************************
+* Function prototypes
+*************************************************************************/
+
+int LZ_Compress( unsigned char *in, unsigned char *out,
+ unsigned int insize );
+int LZ_CompressFast( unsigned char *in, unsigned char *out,
+ unsigned int insize, unsigned int *work );
+void LZ_Uncompress( unsigned char *in, unsigned char *out,
+ unsigned int insize );
+
+
+#endif /* _lz_h_ */
diff --git a/backends/platform/ds/arm9/source/console2.h b/backends/platform/ds/arm9/source/console2.h
new file mode 100644
index 0000000000..6b9a677cc7
--- /dev/null
+++ b/backends/platform/ds/arm9/source/console2.h
@@ -0,0 +1,124 @@
+//////////////////////////////////////////////////////////////////////
+//
+// consol.h --provides basic consol type print functionality
+//
+// version 0.1, February 14, 2005
+//
+// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto)
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any
+// damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any
+// purpose, including commercial applications, and to alter it and
+// redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you
+// must not claim that you wrote the original software. If you use
+// this software in a product, an acknowledgment in the product
+// documentation would be appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and
+// must not be misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+// Changelog:
+// 0.1: First version
+// 0.2: Fixed sprite mapping bug. 1D mapping should work now.
+// Changed some register defines for consistency.
+//
+//////////////////////////////////////////////////////////////////////
+#ifndef CONSOLE_H2
+#define CONSOLE_H2
+
+#define CONSOLE_USE_COLOR255 16
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth);
+void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth);
+
+void consolePrintf(const char* s, ...);
+
+void consolePrintSet(int x, int y);
+
+void consolePrintChar(char c);
+
+void consolePutString(int x, int y, char* s);
+void consolePutInt(int x, int y, int d);
+void consolePutX(int x, int y, int d);
+void consolePutChar(int x, int y, char c);
+void consolePutBin(int x, int y, int b);
+
+void consoleClear(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+//////////////////////////////////////////////////////////////////////
+//
+// consol.h --provides basic consol type print functionality
+//
+// version 0.1, February 14, 2005
+//
+// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto)
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any
+// damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any
+// purpose, including commercial applications, and to alter it and
+// redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you
+// must not claim that you wrote the original software. If you use
+// this software in a product, an acknowledgment in the product
+// documentation would be appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and
+// must not be misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+// Changelog:
+// 0.1: First version
+// 0.2: Fixed sprite mapping bug. 1D mapping should work now.
+// Changed some register defines for consistency.
+//
+//////////////////////////////////////////////////////////////////////
+#ifndef CONSOLE_H2
+#define CONSOLE_H2
+
+#define CONSOLE_USE_COLOR255 16
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth);
+void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth);
+
+void consolePrintf(const char* s, ...);
+
+void consolePrintSet(int x, int y);
+
+void consolePrintChar(char c);
+
+void consolePutString(int x, int y, char* s);
+void consolePutInt(int x, int y, int d);
+void consolePutX(int x, int y, int d);
+void consolePutChar(int x, int y, char c);
+void consolePutBin(int x, int y, int b);
+
+void consoleClear(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp
new file mode 100644
index 0000000000..b42d9ed15e
--- /dev/null
+++ b/backends/platform/ds/arm9/source/dsmain.cpp
@@ -0,0 +1,3888 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <nds.h>
+
+#include <ARM9/console.h> //basic print funcionality
+
+#include <stdlib.h>
+#include "dsmain.h"
+#include "string.h"
+#include "system.h"
+#include "osystem_ds.h"
+#include "icons_raw.h"
+#include "gba_nds_fat.h"
+#include "disc_io.h"
+#include "config-manager.h"
+#include "engines/scumm/scumm.h"
+#include "keyboard_raw.h"
+#include "keyboard_pal_raw.h"
+#define V16(a, b) ((a << 12) | b)
+#include "touchkeyboard.h"
+#include "registers_alt.h"
+//#include "compact_flash.h"
+#include "dsoptions.h"
+
+namespace DS {
+
+// From console.c in NDSLib
+
+//location of cursor
+extern u8 row;
+extern u8 col;
+
+// Mouse mode
+enum MouseMode {
+ MOUSE_LEFT, MOUSE_RIGHT, MOUSE_HOVER, MOUSE_NUM_MODES
+};
+
+// Defines
+#define FRAME_TIME 17
+#define SCUMM_GAME_HEIGHT 142
+#define SCUMM_GAME_WIDTH 232
+
+int textureID;
+u16* texture;
+
+int frameCount;
+int currentTimeMillis;
+
+// Timer Callback
+int callbackInterval;
+int callbackTimer;
+OSystem::TimerProc callback;
+
+// Scaled
+bool scaledMode;
+int scX;
+int scY;
+
+int subScX;
+int subScY;
+int subScTargetX;
+int subScTargetY;
+int subScreenWidth = SCUMM_GAME_WIDTH;
+int subScreenHeight = SCUMM_GAME_HEIGHT;
+int subScreenScale = 256;
+
+
+
+// Sound
+int bufferSize;
+s16* soundBuffer;
+int bufferFrame;
+int bufferRate;
+int bufferSamples;
+bool soundHiPart;
+
+// Events
+int lastEventFrame;
+bool indyFightState;
+bool indyFightRight;
+
+OSystem::SoundProc soundCallback;
+void* soundParam;
+int lastCallbackFrame;
+bool bufferFirstHalf;
+bool bufferSecondHalf;
+
+// Saved buffers
+u8* savedBuffer = NULL;
+bool highBuffer;
+bool displayModeIs8Bit;
+
+// Game id
+u8 gameID;
+
+bool consoleEnable = true;
+bool gameScreenSwap = false;
+
+MouseMode mouseMode;
+
+// Sprites
+SpriteEntry sprites[128];
+SpriteEntry spritesMain[128];
+int tweak;
+
+// Shake
+int shakePos = 0;
+
+// Keyboard
+bool keyboardEnable = false;
+bool leftHandedMode = false;
+bool keyboardIcon = false;
+
+// Touch
+int touchScX, touchScY, touchX, touchY;
+
+// Dragging
+int dragStartX, dragStartY;
+bool dragging = false;
+int dragScX, dragScY;
+
+// Interface styles
+char gameName[32];
+
+// 8-bit surface size
+int gameWidth = 320;
+int gameHeight = 200;
+
+enum controlType {
+ CONT_SCUMM_ORIGINAL,
+ CONT_SCUMM_SAMNMAX,
+ CONT_SKY,
+ CONT_SIMON,
+};
+
+struct gameListType {
+ char gameId[16];
+ controlType control;
+};
+
+#define NUM_SUPPORTED_GAMES 15
+
+gameListType gameList[NUM_SUPPORTED_GAMES] = {
+ // Unknown game - use normal SCUMM controls
+ {"unknown", CONT_SCUMM_ORIGINAL},
+
+ // SCUMM games
+ {"maniac", CONT_SCUMM_ORIGINAL},
+ {"zak", CONT_SCUMM_ORIGINAL},
+ {"loom", CONT_SCUMM_ORIGINAL},
+ {"indy3", CONT_SCUMM_ORIGINAL},
+ {"atlantis", CONT_SCUMM_ORIGINAL},
+ {"monkey", CONT_SCUMM_ORIGINAL},
+ {"monkey2", CONT_SCUMM_ORIGINAL},
+ {"tentacle", CONT_SCUMM_ORIGINAL},
+ {"samnmax", CONT_SCUMM_SAMNMAX},
+
+ // Non-SCUMM games
+ {"sky", CONT_SKY},
+ {"simon1", CONT_SIMON},
+ {"simon2", CONT_SIMON},
+ {"gob1", CONT_SCUMM_ORIGINAL},
+ {"queen", CONT_SCUMM_ORIGINAL}
+};
+
+gameListType* currentGame = NULL;
+
+// Stylus
+#define ABS(x) ((x)>0?(x):-(x))
+
+bool penDown;
+bool penHeld;
+bool penReleased;
+bool penDownLastFrame;
+f32 penX, penY;
+int keysDownSaved;
+int keysReleasedSaved;
+
+bool penDownSaved;
+bool penReleasedSaved;
+int penDownFrames;
+int touchXOffset = 0;
+int touchYOffset = 0;
+
+u16 savedPalEntry255 = RGB15(31, 31, 31);
+
+
+extern "C" int scummvm_main(int argc, char *argv[]);
+void updateStatus();
+
+TransferSound soundControl;
+
+//plays an 8 bit mono sample at 11025Hz
+void playSound(const void* data, u32 length, bool loop, bool adpcm, int rate)
+{
+
+ if (!IPC->soundData) {
+ soundControl.count = 0;
+ }
+
+ soundControl.data[soundControl.count].data = data;
+ soundControl.data[soundControl.count].len = length | (loop? 0x80000000: 0x00000000);
+ soundControl.data[soundControl.count].rate = rate; // 367 samples per frame
+ soundControl.data[soundControl.count].pan = 64;
+ soundControl.data[soundControl.count].vol = 127;
+ soundControl.data[soundControl.count].format = adpcm? 2: 0;
+
+ soundControl.count++;
+
+ DC_FlushAll();
+ IPC->soundData = &soundControl;
+}
+
+void stopSound(int channel) {
+ playSound(NULL, 0, false, false, -channel);
+}
+
+void updateOAM() {
+ DC_FlushAll();
+ dmaCopy(sprites, OAM_SUB, 128 * sizeof(SpriteEntry));
+ dmaCopy(spritesMain, OAM, 128 * sizeof(SpriteEntry));
+}
+
+void setGameSize(int width, int height) {
+ gameWidth = width;
+ gameHeight = height;
+}
+
+int getGameWidth() {
+ return gameWidth;
+}
+
+int getGameHeight() {
+ return gameHeight;
+}
+
+void initSprites() {
+ for(int i = 0; i < 128; i++) {
+ sprites[i].attribute[0] = ATTR0_DISABLED;
+ sprites[i].attribute[1] = 0;
+ sprites[i].attribute[2] = 0;
+ sprites[i].attribute[3] = 0;
+ }
+
+ for(int i = 0; i < 128; i++) {
+ spritesMain[i].attribute[0] = ATTR0_DISABLED;
+ spritesMain[i].attribute[1] = 0;
+ spritesMain[i].attribute[2] = 0;
+ spritesMain[i].attribute[3] = 0;
+ }
+
+ updateOAM();
+}
+
+
+void saveGameBackBuffer() {
+#ifdef DISABLE_SCUMM
+ if (savedBuffer == NULL) savedBuffer = new u8[gameWidth * gameHeight];
+ for (int r = 0; r < 200; r++) {
+ memcpy(savedBuffer + (r * gameWidth), ((u8 *) (get8BitBackBuffer())) + (r * 512), gameWidth);
+ }
+#endif
+}
+
+void restoreGameBackBuffer() {
+#ifdef DISABLE_SCUMM
+ if (savedBuffer) {
+ for (int r = 0; r < 200; r++) {
+ memcpy(((u8 *) (BG_GFX_SUB)) + (r * 512), savedBuffer + (r * gameWidth), gameWidth);
+ memcpy(((u8 *) (get8BitBackBuffer())) + (r * 512), savedBuffer + (r * gameWidth), gameWidth);
+ }
+ delete savedBuffer;
+ savedBuffer = NULL;
+ }
+#endif
+
+#ifndef DISABLE_SCUMM
+ memset(get8BitBackBuffer(), 0, 512 * 256);
+ memset(BG_GFX_SUB, 0, 512 * 256);
+ if (Scumm::g_scumm) {
+ Scumm::g_scumm->markRectAsDirty(Scumm::kMainVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1);
+ Scumm::g_scumm->markRectAsDirty(Scumm::kTextVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1);
+ Scumm::g_scumm->markRectAsDirty(Scumm::kVerbVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1);
+ }
+#endif
+
+}
+
+
+void initGame() {
+ // This is a good time to check for left handed mode since the mode change is done as the game starts.
+ // There's probably a better way, but hey.
+// consolePrintf("initing game\n");
+
+ setOptions();
+
+ //strcpy(gameName, ConfMan.getActiveDomain().c_str());
+ strcpy(gameName, ConfMan.get("gameid").c_str());
+// consolePrintf("\n\n\n\nCurrent game: '%s' %d\n", gameName, gameName[0]);
+
+ currentGame = &gameList[0]; // Default game
+
+ for (int r = 0; r < NUM_SUPPORTED_GAMES; r++) {
+ if (!stricmp(gameName, gameList[r].gameId)) {
+ currentGame = &gameList[r];
+// consolePrintf("Game list num: %d\n", currentGame);
+ }
+ }
+
+
+}
+
+void setLeftHanded(bool enable) {
+ leftHandedMode = enable;
+}
+
+void setTouchXOffset(int x) {
+ touchXOffset = x;
+}
+
+void setTouchYOffset(int y) {
+ touchYOffset = y;
+}
+
+void setUnscaledMode(bool enable) {
+ scaledMode = !enable;
+}
+
+void displayMode8Bit() {
+
+ u16 buffer[32 * 32];
+
+ setKeyboardEnable(false);
+
+ if (!displayModeIs8Bit) {
+ for (int r = 0; r < 32 * 32; r++) {
+ buffer[r] = ((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r];
+ }
+ }
+
+
+
+ videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);
+ videoSetModeSub(MODE_3_2D /*| DISPLAY_BG0_ACTIVE*/ | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text
+
+ vramSetBankA(VRAM_A_MAIN_BG_0x6000000);
+ vramSetBankB(VRAM_B_MAIN_BG_0x6020000);
+
+ vramSetBankC(VRAM_C_SUB_BG_0x6200000);
+ vramSetBankD(VRAM_D_MAIN_BG_0x6040000);
+
+ vramSetBankH(VRAM_H_LCD);
+
+ BG3_CR = BG_BMP8_512x256 | BG_BMP_BASE(8);
+
+
+
+ BG3_XDX = (int) (((float) (gameWidth) / 256.0f) * 256);
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = (int) ((200.0f / 192.0f) * 256);
+
+ SUB_BG3_CR = BG_BMP8_512x256;
+
+
+
+ SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256);
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256);
+
+
+ // Do text stuff
+ BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1);
+ BG0_Y0 = 0;
+
+ // Restore palette entry used by text in the front-end
+ PALETTE_SUB[255] = savedPalEntry255;
+
+ consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16);
+ consolePrintSet(0, 23);
+
+ if (!displayModeIs8Bit) {
+ for (int r = 0; r < 32 * 32; r++) {
+ ((u16 *) SCREEN_BASE_BLOCK(0))[r] = buffer[r];
+ }
+// dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK(0), buffer, 32 * 32 * 2);
+ }
+
+
+ if (!displayModeIs8Bit) restoreGameBackBuffer();
+ displayModeIs8Bit = true;
+
+ POWER_CR &= ~POWER_SWAP_LCDS;
+
+ keyboardEnable = false;
+ initGame();
+
+}
+
+void setGameID(int id) {
+ gameID = id;
+}
+
+void dummyHandler() {
+ REG_IF = IRQ_VBLANK;
+}
+
+void checkSleepMode() {
+ if (IPC->performArm9SleepMode) {
+
+ consolePrintf("ARM9 Entering sleep mode\n");
+
+ int intSave = REG_IE;
+ irqSet(IRQ_VBLANK, dummyHandler);
+// int irqHandlerSave = (int) IRQ_HANDLER;
+ REG_IE = IRQ_VBLANK;
+ //IRQ_HANDLER = dummyHandler;
+
+ int powerSave = POWER_CR;
+ POWER_CR &= ~POWER_ALL;
+
+ while (IPC->performArm9SleepMode) {
+ swiWaitForVBlank();
+ }
+
+ POWER_CR = powerSave;
+// IRQ_HANDLER = (void (*)()) irqHandlerSave;
+ irqSet(IRQ_VBLANK, VBlankHandler);
+ REG_IE = intSave;
+
+ consolePrintf("ARM9 Waking from sleep mode\n");
+ }
+}
+
+void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor) {
+ if (currentGame->control != CONT_SCUMM_SAMNMAX)
+ return;
+
+ uint16 border = RGB15(24,24,24) | 0x8000;
+
+
+ int off = 48*64;
+ memset(SPRITE_GFX_SUB+off, 0, 64*64*2);
+
+ int pos = 190 - (w+2);
+
+
+
+ // make border
+ for (uint i=0; i<w+2; i++) {
+ SPRITE_GFX_SUB[off+i] = border;
+ SPRITE_GFX_SUB[off+(31)*64+i] = border;
+ }
+ for (uint i=1; i<31; i++) {
+ SPRITE_GFX_SUB[off+(i*64)] = border;
+ SPRITE_GFX_SUB[off+(i*64)+(w+1)] = border;
+ }
+
+ int offset = (32 - h) >> 1;
+
+ for (uint y=0; y<h; y++) {
+ for (uint x=0; x<w; x++) {
+ int color = icon[y*w+x];
+ if (color == keycolor) {
+ SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = 0x8000; // black background
+ } else {
+ SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = BG_PALETTE[color] | 0x8000;
+ }
+ }
+ }
+
+ sprites[1].attribute[0] = ATTR0_BMP | 150;
+ sprites[1].attribute[1] = ATTR1_SIZE_64 | pos;
+ sprites[1].attribute[2] = ATTR2_ALPHA(1) | 48;
+}
+
+
+
+
+void displayMode16Bit() {
+
+ u16 buffer[32 * 32 * 2];
+
+
+ if (displayModeIs8Bit) {
+ saveGameBackBuffer();
+ for (int r = 0; r < 32 * 32; r++) {
+ buffer[r] = ((u16 *) SCREEN_BASE_BLOCK(0))[r];
+ }
+ }
+
+
+ videoSetMode(MODE_5_2D | /*DISPLAY_BG0_ACTIVE |*/ DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);
+ videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE |/* DISPLAY_BG1_ACTIVE |*/ DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text
+
+ vramSetBankA(VRAM_A_MAIN_BG);
+ vramSetBankB(VRAM_B_MAIN_BG);
+ vramSetBankC(VRAM_C_MAIN_BG);
+ vramSetBankD(VRAM_D_MAIN_BG);
+ vramSetBankH(VRAM_H_SUB_BG);
+
+ BG3_CR = BG_BMP16_512x256;
+ highBuffer = false;
+
+ BG3_XDX = (int) (1.25f * 256);
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = (int) ((200.0f / 192.0f) * 256);
+
+ memset(BG_GFX, 0, 512 * 256 * 2);
+
+ savedPalEntry255 = PALETTE_SUB[255];
+ PALETTE_SUB[255] = RGB15(31,31,31);//by default font will be rendered with color 255
+
+ // Do text stuff
+ SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0);
+ SUB_BG0_Y0 = 0;
+
+ consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);
+
+ if (displayModeIs8Bit) {
+ //dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK_SUB(0), buffer, 32 * 32 * 2);
+ for (int r = 0; r < 32 * 32; r++) {
+ ((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r] = buffer[r];
+ }
+ }
+
+ consolePrintSet(0, 23);
+ consolePrintf("\n");
+
+ // Show keyboard
+ SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12);
+ //drawKeyboard(1, 12);
+
+ POWER_CR &= ~POWER_SWAP_LCDS;
+
+
+
+ displayModeIs8Bit = false;
+}
+
+
+void displayMode16BitFlipBuffer() {
+ if (!displayModeIs8Bit) {
+ u16* back = get16BitBackBuffer();
+
+// highBuffer = !highBuffer;
+// BG3_CR = BG_BMP16_512x256 | BG_BMP_RAM(highBuffer? 1: 0);
+
+ for (int r = 0; r < 512 * 256; r++) {
+ *(BG_GFX + r) = *(back + r);
+ }
+ }
+}
+
+void setShakePos(int shakePos) {
+ shakePos = shakePos;
+}
+
+
+u16* get16BitBackBuffer() {
+ return BG_GFX + 0x20000;
+}
+
+u16* get8BitBackBuffer() {
+ return BG_GFX + 0x10000; // 16bit qty!
+}
+
+void setSoundProc(OSystem::SoundProc proc, void* param) {
+// consolePrintf("Set sound callback");
+ soundCallback = proc;
+ soundParam = param;
+}
+
+// The sound system in ScummVM seems to always return stereo interleaved samples.
+// Here, I'm treating an 11Khz stereo stream as a 22Khz mono stream, which works sorta ok, but is
+// a horrible bodge. Any advice on how to change the engine to output mono would be greatly
+// appreciated.
+void doSoundCallback() {
+ if ((soundCallback)) {
+ lastCallbackFrame = frameCount;
+
+ for (int r = IPC->playingSection; r < IPC->playingSection + 4; r++) {
+ int chunk = r & 3;
+
+ if (IPC->fillNeeded[chunk]) {
+ IPC->fillNeeded[chunk] = false;
+ DC_FlushAll();
+ soundCallback(soundParam, (byte *) (soundBuffer + ((bufferSamples >> 2) * chunk)), bufferSamples >> 1);
+ IPC->fillNeeded[chunk] = false;
+ DC_FlushAll();
+ }
+
+ }
+
+ }
+}
+
+void doTimerCallback() {
+ if (callback) {
+ if (callbackTimer <= 0) {
+ callbackTimer += callbackInterval;
+ callback(callbackInterval);
+ }
+ }
+}
+
+void soundUpdate() {
+ if ((bufferFrame == 0)) {
+// playSound(soundBuffer, (bufferSamples * 2), true);
+ }
+// consolePrintf("%x\n", IPC->test);
+
+
+ if (bufferFrame == 0) {
+// bufferFirstHalf = true;
+ }
+ if (bufferFrame == bufferSize >> 1) {
+ //bufferSecondHalf = true;
+ }
+
+ bufferFrame++;
+ if (bufferFrame == bufferSize) {
+ bufferFrame = 0;
+ }
+}
+
+void memoryReport() {
+ int r = 0;
+ int* p;
+ do {
+ p = (int *) malloc(r * 8192);
+ free(p);
+ r++;
+ } while ((p) && (r < 512));
+
+ int t = -1;
+ void* block[1024];
+ do {
+ t++;
+ block[t] = (int *) malloc(4096);
+ } while ((t < 1024) && (block[t]));
+
+ for (int q = 0; q < t; q++) {
+ free(block[q]);
+ }
+
+ consolePrintf("Free: %dK, Largest: %dK\n", t * 4, r * 8);
+}
+
+
+void addIndyFightingKeys() {
+ OSystem_DS* system = OSystem_DS::instance();
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.flags = 0;
+
+ if ((getKeysDown() & KEY_L)) {
+ indyFightRight = false;
+ }
+
+ if ((getKeysDown() & KEY_R)) {
+ indyFightRight = true;
+ }
+
+ if ((getKeysDown() & KEY_UP)) {
+ event.kbd.keycode = '8';
+ event.kbd.ascii = '8';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_LEFT)) {
+ event.kbd.keycode = '4';
+ event.kbd.ascii = '4';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_RIGHT)) {
+ event.kbd.keycode = '6';
+ event.kbd.ascii = '6';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_DOWN)) {
+ event.kbd.keycode = '2';
+ event.kbd.ascii = '2';
+ system->addEvent(event);
+ }
+
+ if (indyFightRight) {
+
+ if ((getKeysDown() & KEY_X)) {
+ event.kbd.keycode = '9';
+ event.kbd.ascii = '9';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_A)) {
+ event.kbd.keycode = '6';
+ event.kbd.ascii = '6';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_B)) {
+ event.kbd.keycode = '3';
+ event.kbd.ascii = '3';
+ system->addEvent(event);
+ }
+
+ } else {
+
+ if ((getKeysDown() & KEY_X)) {
+ event.kbd.keycode = '7';
+ event.kbd.ascii = '7';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_A)) {
+ event.kbd.keycode = '4';
+ event.kbd.ascii = '4';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_B)) {
+ event.kbd.keycode = '1';
+ event.kbd.ascii = '1';
+ system->addEvent(event);
+ }
+
+ }
+
+
+ if ((getKeysDown() & KEY_Y)) {
+ event.kbd.keycode = '5';
+ event.kbd.ascii = '5';
+ system->addEvent(event);
+ }
+}
+
+
+void setKeyboardEnable(bool en) {
+ if (en == keyboardEnable) return;
+ keyboardEnable = en;
+ u16* backupBank = (u16 *) 0x6040000;
+
+ if (keyboardEnable) {
+
+
+ DS::drawKeyboard(1, 12, backupBank);
+
+
+ SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12);
+
+ if (displayModeIs8Bit) {
+ SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer
+ SUB_DISPLAY_CR &= ~DISPLAY_BG3_ACTIVE; // Turn off game layer
+ } else {
+ SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer
+ SUB_DISPLAY_CR &= ~DISPLAY_BG0_ACTIVE; // Turn off console layer
+ }
+ lcdSwap();
+ } else {
+
+
+ // Restore the palette that the keyboard has used
+ for (int r = 0; r < 256; r++) {
+ BG_PALETTE_SUB[r] = BG_PALETTE[r];
+ }
+
+
+ //restoreVRAM(1, 12, backupBank);
+
+ if (displayModeIs8Bit) {
+ // Copy the sub screen VRAM from the top screen - they should always be
+ // the same.
+ for (int r = 0; r < (512 * 256) >> 1; r++) {
+ BG_GFX_SUB[r] = get8BitBackBuffer()[r];
+ }
+
+ SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer
+ SUB_DISPLAY_CR |= DISPLAY_BG3_ACTIVE; // Turn on game layer
+ } else {
+ SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer
+ SUB_DISPLAY_CR |= DISPLAY_BG0_ACTIVE; // Turn on console layer
+ }
+
+ lcdSwap();
+ }
+}
+
+bool getKeyboardEnable() {
+ return keyboardEnable;
+}
+
+bool getIsDisplayMode8Bit() {
+ return displayModeIs8Bit;
+}
+
+void addEventsToQueue() {
+ OSystem_DS* system = OSystem_DS::instance();
+ OSystem::Event event;
+
+
+
+
+ if (system->isEventQueueEmpty()) {
+
+/*
+ if (getKeysDown() & KEY_L) {
+ tweak--;
+ consolePrintf("Tweak: %d\n", tweak);
+ IPC->tweakChanged = true;
+ }
+
+
+ if (getKeysDown() & KEY_R) {
+ tweak++;
+ consolePrintf("Tweak: %d\n", tweak);
+ IPC->tweakChanged = true;
+ }
+ */
+ if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) {
+ memoryReport();
+ }
+
+ if (displayModeIs8Bit) {
+
+ if (!indyFightState) {
+
+
+ if ((getKeysDown() & KEY_B) && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) {
+ // consolePrintf("Pressing Esc");
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 27;
+ event.kbd.ascii = 27;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+ }
+
+ }
+
+
+
+ if ((!getIndyFightState()) && (getKeysDown() & KEY_Y)) {
+ consoleEnable = !consoleEnable;
+ if (displayModeIs8Bit) {
+ displayMode8Bit();
+ } else {
+ displayMode16Bit();
+ }
+ }
+
+
+ if (!((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (!getIndyFightState())) {
+
+ if ((getKeysDown() & KEY_A) && (!indyFightState)) {
+ gameScreenSwap = !gameScreenSwap;
+ }
+
+ if (!getPenHeld() || (mouseMode != MOUSE_HOVER)) {
+ if (getKeysDown() & KEY_LEFT) {
+ mouseMode = MOUSE_LEFT;
+ }
+ if (getKeysDown() & KEY_RIGHT) {
+ if (currentGame->control != CONT_SCUMM_SAMNMAX) {
+ mouseMode = MOUSE_RIGHT;
+ } else {
+ // If we're playing sam and max, click and release the right mouse
+ // button to change verb
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_RBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_RBUTTONUP;
+ system->addEvent(event);
+ }
+ }
+ if (getKeysDown() & KEY_UP) {
+ mouseMode = MOUSE_HOVER;
+ }
+ }
+
+
+
+ }
+
+ if ((getKeysDown() & KEY_SELECT)) {
+ //scaledMode = !scaledMode;
+ //scY = 4;
+ showOptionsDialog();
+ }
+
+
+ }
+
+ if (!getIndyFightState() && !((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (getKeysDown() & KEY_X)) {
+ setKeyboardEnable(!keyboardEnable);
+ }
+
+ updateStatus();
+
+ OSystem::Event event;
+
+ if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) {
+ event.type = OSystem::EVENT_MOUSEMOVE;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ //consolePrintf("x=%d y=%d \n", getPenX(), getPenY());
+ }
+
+ if (!keyboardEnable) {
+ if ((mouseMode != MOUSE_HOVER) || (!displayModeIs8Bit)) {
+ if (getPenDown() && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) {
+ event.type = ((mouseMode == MOUSE_LEFT) || (!displayModeIs8Bit))? OSystem::EVENT_LBUTTONDOWN: OSystem::EVENT_RBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+
+ if (getPenReleased()) {
+ event.type = mouseMode == MOUSE_LEFT? OSystem::EVENT_LBUTTONUP: OSystem::EVENT_RBUTTONUP;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+ } else {
+ // In hover mode, D-pad left and right click the mouse when the pen is on the screen
+
+ if (getPenHeld()) {
+ if (getKeysDown() & KEY_LEFT) {
+ event.type = OSystem::EVENT_LBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+ /* if (getKeysReleased() & KEY_LEFT) {
+ event.type = OSystem::EVENT_LBUTTONUP;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }*/
+
+ if (getKeysDown() & KEY_RIGHT) {
+ event.type = OSystem::EVENT_RBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+ /*if (getKeysReleased() & KEY_RIGHT) {
+ event.type = OSystem::EVENT_RBUTTONUP;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }*/
+ }
+ }
+
+ if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R)) && (displayModeIs8Bit)) {
+ // Controls specific to the control method
+
+
+ if (currentGame->control == CONT_SKY) {
+ // Extra controls for Benieth a Steel Sky
+ if ((getKeysDown() & KEY_DOWN)) {
+ penY = 0;
+ penX = 0; // Show inventory by moving mouse onto top line
+ }
+ }
+
+ if (currentGame->control == CONT_SIMON) {
+ // Extra controls for Simon the Sorcerer
+ if ((getKeysDown() & KEY_DOWN)) {
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = '#'; // F10 or # - show hotspots
+ event.kbd.ascii = '#';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+// consolePrintf("F10\n");
+ }
+ }
+
+ if (currentGame->control == CONT_SCUMM_ORIGINAL) {
+ // Extra controls for Scumm v1-5 games
+ if ((getKeysDown() & KEY_DOWN)) {
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = '.'; // Full stop - skips current dialogue line
+ event.kbd.ascii = '.';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+ }
+
+ if (indyFightState) {
+ addIndyFightingKeys();
+ }
+
+ }
+
+ }
+ }
+
+ if (!displayModeIs8Bit) {
+ // Front end controls
+
+ if (leftHandedSwap(getKeysDown()) & KEY_UP) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = SDLK_UP;
+ event.kbd.ascii = 0;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+ }
+
+ if (leftHandedSwap(getKeysDown()) & KEY_DOWN) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = SDLK_DOWN;
+ event.kbd.ascii = 0;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+ }
+
+ if (leftHandedSwap(getKeysDown()) & KEY_A) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = SDLK_RETURN;
+ event.kbd.ascii = 0;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+ }
+
+ }
+
+
+ if ((getKeysDown() & KEY_START)) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 319; // F5
+ event.kbd.ascii = 319;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+/*
+ event.type = OSystem::EVENT_KEYUP;
+ event.kbd.keycode = 319; // F5
+ event.kbd.ascii = 319;
+ system->addEvent(event);*/
+
+// consolePrintf("Pressing F5");
+ }
+
+
+ if (keyboardEnable) {
+ DS::addKeyboardEvents();
+ }
+
+ consumeKeys();
+
+ consumePenEvents();
+
+ }
+}
+
+void updateStatus() {
+ int offs;
+
+ if (displayModeIs8Bit) {
+ switch (mouseMode) {
+ case MOUSE_LEFT: {
+ offs = 16;
+ break;
+ }
+ case MOUSE_RIGHT: {
+ offs = 32;
+ break;
+ }
+ case MOUSE_HOVER: {
+ offs = 0;
+ break;
+ }
+ default: {
+ // Nothing!
+ offs = 0;
+ break;
+ }
+ }
+
+
+ sprites[0].attribute[0] = ATTR0_BMP | 150;
+ sprites[0].attribute[1] = ATTR1_SIZE_32 | 208;
+ sprites[0].attribute[2] = ATTR2_ALPHA(1)| offs;
+
+ if (indyFightState) {
+ sprites[2].attribute[0] = ATTR0_BMP | 150;
+ sprites[2].attribute[1] = ATTR1_SIZE_32 | (190 - 32) | (indyFightRight? 0: ATTR1_FLIP_X);
+ sprites[2].attribute[2] = ATTR2_ALPHA(1)| 48;
+ } else {
+ sprites[2].attribute[0] = ATTR0_DISABLED;
+ sprites[2].attribute[1] = 0;
+ sprites[2].attribute[2] = 0;
+ }
+ } else {
+ sprites[0].attribute[0] = ATTR0_DISABLED;
+ sprites[1].attribute[0] = ATTR0_DISABLED;
+ sprites[2].attribute[0] = ATTR0_DISABLED;
+ sprites[3].attribute[0] = ATTR0_DISABLED;
+ }
+
+ if ((keyboardIcon) && (!keyboardEnable) && (!displayModeIs8Bit)) {
+ spritesMain[0].attribute[0] = ATTR0_BMP | 160;
+ spritesMain[0].attribute[1] = ATTR1_SIZE_32 | 0;
+ spritesMain[0].attribute[2] = ATTR2_ALPHA(1) | 64;
+ } else {
+ spritesMain[0].attribute[0] = ATTR0_DISABLED;
+ spritesMain[0].attribute[1] = 0;
+ spritesMain[0].attribute[2] = 0;
+ spritesMain[0].attribute[3] = 0;
+ }
+
+}
+
+void soundBufferEmptyHandler() {
+ REG_IF = IRQ_TIMER2;
+
+ if (soundHiPart) {
+// bufferSecondHalf = true;
+ } else {
+// bufferFirstHalf = true;
+ }
+
+ soundHiPart = !soundHiPart;
+}
+
+void setMainScreenScroll(int x, int y) {
+ if (gameScreenSwap) {
+ SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ SUB_BG3_CY = y;
+ } else {
+ BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ BG3_CY = y;
+
+ touchX = x >> 8;
+ touchY = y >> 8;
+ }
+}
+
+void setMainScreenScale(int x, int y) {
+ if (gameScreenSwap) {
+ SUB_BG3_XDX = x;
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = y;
+ } else {
+ BG3_XDX = x;
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = y;
+
+ touchScX = x;
+ touchScY = y;
+ }
+}
+
+void setZoomedScreenScroll(int x, int y) {
+ if (gameScreenSwap) {
+ BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ BG3_CY = y;
+
+ touchX = x >> 8;
+ touchY = y >> 8;
+ } else {
+ SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ SUB_BG3_CY = y;
+ }
+}
+
+void setZoomedScreenScale(int x, int y) {
+ if (gameScreenSwap) {
+ BG3_XDX = x;
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = y;
+
+ touchScX = x;
+ touchScY = y;
+ } else {
+ SUB_BG3_XDX = x;
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = y;
+ }
+}
+
+void VBlankHandler(void) {
+// BG_PALETTE[0] = RGB15(31, 31, 31);
+// if (*((int *) (0x023FFF00)) != 0xBEEFCAFE) {
+ // consolePrintf("Guard band overwritten!");
+// }
+
+// consolePri ntf("X:%d Y:%d\n", getPenX(), getPenY());
+
+
+ IPC->tweak = tweak;
+ soundUpdate();
+
+
+
+
+ if ((!gameScreenSwap) && (!(getKeysHeld() & KEY_L) && !(getKeysHeld() & KEY_R))) {
+ if (currentGame) {
+ if (currentGame->control != CONT_SCUMM_SAMNMAX) {
+ if (getPenHeld() && (getPenY() < SCUMM_GAME_HEIGHT)) {
+ setTopScreenTarget(getPenX(), getPenY());
+ }
+ } else {
+ if (getPenHeld()) {
+ setTopScreenTarget(getPenX(), getPenY());
+ }
+ }
+ }
+ }
+
+
+ penUpdate();
+ keysUpdate();
+
+
+ frameCount++;
+
+
+
+ if (callback) {
+ callbackTimer -= FRAME_TIME;
+ }
+
+ if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) {
+
+ if ((!dragging) && (getPenHeld()) && (penDownFrames > 5)) {
+ dragging = true;
+ dragStartX = penX;
+ dragStartY = penY;
+
+ if (gameScreenSwap) {
+ dragScX = subScTargetX;
+ dragScY = subScTargetY;
+ } else {
+ dragScX = scX;
+ dragScY = scY;
+ }
+
+
+ }
+
+ if ((dragging) && (!getPenHeld())) {
+ dragging = false;
+ }
+
+ if (dragging) {
+
+ if (gameScreenSwap) {
+ subScTargetX = dragScX + ((dragStartX - penX) << 8);
+ subScTargetY = dragScY + ((dragStartY - penY) << 8);
+ } else {
+ scX = dragScX + ((dragStartX - penX));
+ scY = dragScY + ((dragStartY - penY));
+ }
+
+// consolePrintf("X:%d Y:%d\n", dragStartX - penX, dragStartY - penY);
+ }
+ }
+
+
+/* if ((frameCount & 1) == 0) {
+ SUB_BG3_CX = subScX;
+ } else {
+ SUB_BG3_CX = subScX + 64;
+ }
+
+ SUB_BG3_CY = subScY + (shakePos << 8);*/
+
+ /*SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256);
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256);*/
+
+
+ if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) {
+ if ((getKeysHeld() & KEY_A) && (subScreenScale < 256)) {
+ subScreenScale += 3;
+ }
+
+ if ((getKeysHeld() & KEY_B) && (subScreenScale > 128)) {
+ subScreenScale -=3;
+ }
+
+ int xCenter = subScTargetX + ((subScreenWidth >> 1) << 8);
+ int yCenter = subScTargetY + ((subScreenHeight >> 1) << 8);
+
+ subScreenWidth = SCUMM_GAME_WIDTH * subScreenScale >> 8;
+ subScreenHeight = SCUMM_GAME_HEIGHT * subScreenScale >> 8;
+
+ subScTargetX = xCenter - ((subScreenWidth >> 1) << 8);
+ subScTargetY = yCenter - ((subScreenHeight >> 1) << 8);
+
+
+
+
+ if (subScTargetX < 0) subScTargetX = 0;
+ if (subScTargetX > (gameWidth - subScreenWidth) << 8) subScTargetX = (gameWidth - subScreenWidth) << 8;
+
+ if (subScTargetY < 0) subScTargetY = 0;
+ if (subScTargetY > (gameHeight - subScreenHeight) << 8) subScTargetY = (gameHeight - subScreenHeight) << 8;
+ }
+
+ subScX += (subScTargetX - subScX) >> 2;
+ subScY += (subScTargetY - subScY) >> 2;
+
+ if (displayModeIs8Bit) {
+
+ if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) {
+
+ int offsX = 0, offsY = 0;
+
+
+ if (getKeysHeld() & KEY_LEFT) {
+ offsX -= 1;
+ }
+
+ if (getKeysHeld() & KEY_RIGHT) {
+ offsX += 1;
+ }
+
+ if (getKeysHeld() & KEY_UP) {
+ offsY -= 1;
+ }
+
+ if (getKeysHeld() & KEY_DOWN) {
+ offsY += 1;
+ }
+
+ if (((gameScreenSwap) && (getKeysHeld() & KEY_L)) || ((!gameScreenSwap) && (getKeysHeld() & KEY_R))) {
+ subScTargetX += offsX << 8;
+ subScTargetY += offsY << 8;
+ } else {
+ scX += offsX;
+ scY += offsY;
+ }
+ }
+
+ if (!scaledMode) {
+
+ if (scX + 256 > gameWidth - 1) {
+ scX = gameWidth - 1 - 256;
+ }
+
+ if (scX < 0) {
+ scX = 0;
+ }
+
+ if (scY + 192 > gameHeight - 1) {
+ scY = gameHeight - 1 - 192;
+ }
+
+ if (scY < 0) {
+ scY = 0;
+ }
+
+ setZoomedScreenScroll(subScX, subScY);
+ setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192);
+
+
+ setMainScreenScroll(scX << 8, (scY << 8) + (shakePos << 8));
+ setMainScreenScale(256, 256); // 1:1 scale
+
+ } else {
+
+ if (scY > gameHeight - 192 - 1) {
+ scY = gameHeight - 192 - 1;
+ }
+
+ if (scY < 0) {
+ scY = 0;
+ }
+
+ setZoomedScreenScroll(subScX, subScY);
+ setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192);
+
+ setMainScreenScroll(64, (scY << 8) + (shakePos << 8));
+ setMainScreenScale(320, 256); // 1:1 scale
+
+ }
+ } else {
+ setZoomedScreenScroll(0, 0);
+ setZoomedScreenScale(320, 256);
+
+ setMainScreenScroll(0, 0);
+ setMainScreenScale(320, 256); // 1:1 scale
+ }
+
+ // Enable on screen keyboard when pen taps icon
+ if ((keyboardIcon) && (penX < 32) && (penY > 160) && (penHeld)) {
+ setKeyboardEnable(true);
+ }
+
+ if (keyboardEnable) {
+ if (DS::getKeyboardClosed()) {
+ setKeyboardEnable(false);
+ }
+ }
+
+ updateOAM();
+
+ //PALETTE[0] = RGB15(0, 0, 0);
+ REG_IF = IRQ_VBLANK;
+}
+
+int getMillis() {
+ return currentTimeMillis;
+// return frameCount * FRAME_TIME;
+}
+
+void setTimerCallback(OSystem::TimerProc proc, int interval) {
+// consolePrintf("Set timer proc %x, int %d\n", proc, interval);
+ callback = proc;
+ callbackInterval = interval;
+ callbackTimer = interval;
+}
+
+void timerTickHandler() {
+ REG_IF = IRQ_TIMER0;
+ if ((callback) && (callbackTimer > 0)) {
+ callbackTimer--;
+ }
+ currentTimeMillis++;
+}
+
+void setTalkPos(int x, int y) {
+// if (gameID != Scumm::GID_SAMNMAX) {
+// setTopScreenTarget(x, 0);
+// } else {
+ setTopScreenTarget(x, y);
+// }
+}
+
+void setTopScreenTarget(int x, int y) {
+ subScTargetX = (x - (subScreenWidth >> 1));
+ subScTargetY = (y - (subScreenHeight >> 1));
+
+ if (subScTargetX < 0) subScTargetX = 0;
+ if (subScTargetX > gameWidth - subScreenWidth) subScTargetX = gameWidth - subScreenWidth;
+
+ if (subScTargetY < 0) subScTargetY = 0;
+ if (subScTargetY > gameHeight - subScreenHeight) subScTargetY = gameHeight - subScreenHeight;
+
+ subScTargetX <<=8;
+ subScTargetY <<=8;
+}
+
+void initHardware() {
+ // Guard band
+//((int *) (0x023FFF00)) = 0xBEEFCAFE;
+
+
+ penInit();
+
+ powerON(POWER_ALL);
+/* vramSetBankA(VRAM_A_MAIN_BG);
+ vramSetBankB(VRAM_B_MAIN_BG);
+ vramSetBankC(VRAM_C_SUB_BG); */
+ vramSetBankI(VRAM_I_SUB_SPRITE);
+ vramSetBankG(VRAM_G_MAIN_SPRITE);
+
+ currentTimeMillis = 0;
+
+
+/*
+ // Set up a millisecond counter
+ TIMER0_CR = 0;
+ TIMER0_DATA = 0xFFFF;
+ TIMER0_CR = TIMER_ENABLE | TIMER_CASCADE;
+*/
+
+
+
+ PALETTE[255] = RGB15(0,31,0);
+
+ // Allocate save buffer for game screen
+// savedBuffer = new u8[320 * 200];
+ displayMode16Bit();
+
+ memset(BG_GFX, 0, 512 * 256 * 2);
+ scaledMode = true;
+ scX = 0;
+ scY = 0;
+ subScX = 0;
+ subScY = 0;
+ subScTargetX = 0;
+ subScTargetY = 0;
+
+ //lcdSwap();
+ POWER_CR &= ~POWER_SWAP_LCDS;
+
+ frameCount = 0;
+ callback = NULL;
+
+// vramSetBankH(VRAM_H_SUB_BG);
+
+
+// // Do text stuff
+ //BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1);
+// BG0_Y0 = 48;
+
+ PALETTE[255] = RGB15(31,31,31);//by default font will be rendered with color 255
+
+ //consoleInit() is a lot more flexible but this gets you up and running quick
+// consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16);
+ //consolePrintSet(0, 6);
+
+ //irqs are nice
+ irqInit();
+// irqInitHandler();
+ irqSet(IRQ_VBLANK, VBlankHandler);
+ irqSet(IRQ_TIMER0, timerTickHandler);
+ irqSet(IRQ_TIMER2, soundBufferEmptyHandler);
+
+ irqEnable(IRQ_VBLANK);
+ irqEnable(IRQ_TIMER0);
+ irqEnable(IRQ_TIMER2);
+
+
+ // Set up a millisecond timer
+ TIMER0_CR = 0;
+ TIMER0_DATA = (u32) TIMER_FREQ(1000);
+ TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1 | TIMER_IRQ_REQ;
+ REG_IME = 1;
+
+ PALETTE[255] = RGB15(0,0,31);
+
+ initSprites();
+
+// videoSetModeSub(MODE_3_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text
+
+ // Convert texture from 24bit 888 to 16bit 1555, remembering to set top bit!
+ u8* srcTex = (u8 *) icons_raw;
+ for (int r = 32 * 160 ; r >= 0; r--) {
+ SPRITE_GFX_SUB[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10);
+ SPRITE_GFX[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10);
+ }
+
+ WAIT_CR &= ~(0x0080);
+ REG_WRAM_CNT = 0;
+
+}
+
+
+void setKeyboardIcon(bool enable) {
+ keyboardIcon = enable;
+}
+
+bool getKeyboardIcon() {
+ return keyboardIcon;
+}
+
+
+////////////////////
+// Pen stuff
+////////////////////
+
+
+void penInit() {
+ penDown = false;
+ penHeld = false;
+ penReleased = false;
+ penDownLastFrame = false;
+ penDownSaved = false;
+ penReleasedSaved = false;
+ penDownFrames = 0;
+ consumeKeys();
+}
+
+void penUpdate() {
+
+// if (getKeysHeld() & KEY_L) consolePrintf("%d, %d penX=%d, penY=%d tz=%d\n", IPC->touchXpx, IPC->touchYpx, penX, penY, IPC->touchZ1);
+
+ if ((penDownFrames > 1)) { // Is this right? Dunno, but it works for me.
+
+ if ((penHeld)) {
+ penHeld = true;
+ penDown = false;
+
+ if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) {
+ penX = IPC->touchXpx + touchXOffset;
+ penY = IPC->touchYpx + touchYOffset;
+ }
+
+ } else {
+ penDown = true;
+ penHeld = true;
+ penDownSaved = true;
+
+ //if ( (ABS(penX - IPC->touchXpx) < 10) && (ABS(penY - IPC->touchYpx) < 10) ) {
+ if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) {
+ penX = IPC->touchXpx;
+ penY = IPC->touchYpx;
+ }
+ //}
+ }
+
+ } else {
+ if (penHeld) {
+ penReleased = true;
+ penReleasedSaved = true;
+ } else {
+ penReleased = false;
+ }
+
+ penDown = false;
+ penHeld = false;
+ }
+
+
+ if ((IPC->touchZ1 > 0) || ((penDownFrames == 2)) ) {
+ penDownLastFrame = true;
+ penDownFrames++;
+ } else {
+ penDownLastFrame = false;
+ penDownFrames = 0;
+ }
+
+}
+
+int leftHandedSwap(int keys) {
+ // Start and select are unchanged
+ if (leftHandedMode) {
+ int result = keys & (~(KEY_R | KEY_L | KEY_Y | KEY_A | KEY_B | KEY_X | KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN));
+
+ if (keys & KEY_L) result |= KEY_R;
+ if (keys & KEY_R) result |= KEY_L;
+
+ if (keys & KEY_LEFT) result |= KEY_Y;
+ if (keys & KEY_RIGHT) result |= KEY_A;
+ if (keys & KEY_DOWN) result |= KEY_B;
+ if (keys & KEY_UP) result |= KEY_X;
+
+ if (keys & KEY_Y) result |= KEY_LEFT;
+ if (keys & KEY_A) result |= KEY_RIGHT;
+ if (keys & KEY_B) result |= KEY_DOWN;
+ if (keys & KEY_X) result |= KEY_UP;
+
+ return result;
+ } else {
+ return keys;
+ }
+}
+
+void keysUpdate() {
+ scanKeys();
+ keysDownSaved |= leftHandedSwap(keysDown());
+ keysReleasedSaved |= leftHandedSwap(keysUp());
+}
+
+int getKeysDown() {
+ return keysDownSaved;
+}
+
+int getKeysHeld() {
+ return leftHandedSwap(keysHeld());
+}
+
+int getKeysReleased() {
+ return keysReleasedSaved;
+}
+
+void consumeKeys() {
+ keysDownSaved = 0;
+ keysReleasedSaved = 0;
+}
+
+bool getPenDown() {
+ return penDownSaved;
+}
+
+bool getPenHeld() {
+ return penHeld;
+}
+
+bool getPenReleased() {
+ return penReleasedSaved;
+}
+
+void consumePenEvents() {
+ penDownSaved = false;
+ penReleasedSaved = false;
+}
+
+int getPenX() {
+ int x = ((penX * touchScX) >> 8) + touchX;
+ x = x < 0? 0: (x > gameWidth - 1? gameWidth - 1: x);
+ return x;
+}
+
+int getPenY() {
+ int y = ((penY * touchScY) >> 8) + touchY;
+ y = y < 0? 0: (y > gameHeight - 1? gameHeight - 1: y);
+ return y;
+}
+
+GLvector getPenPos() {
+ GLvector v;
+
+ v.x = (penX * inttof32(1)) / SCREEN_WIDTH;
+ v.y = (penY * inttof32(1)) / SCREEN_HEIGHT;
+
+ return v;
+}
+
+void formatSramOption() {
+ consolePrintf("The following files are present in save RAM:\n");
+ DSSaveFileManager::instance()->listFiles();
+
+ consolePrintf("\nAre you sure you want to\n");
+ consolePrintf("DELETE all files?\n");
+ consolePrintf("A = Yes, X = No\n");
+
+ while (true) {
+ if (keysHeld() & KEY_A) {
+ DSSaveFileManager::instance()->formatSram();
+ consolePrintf("SRAM cleared!\n");
+ return;
+ }
+
+ if (keysHeld() & KEY_X) {
+ consolePrintf("Whew, that was close!\n");
+ return;
+ }
+ }
+}
+
+
+void setIndyFightState(bool st) {
+ indyFightState = st;
+ indyFightRight = true;
+}
+
+bool getIndyFightState() {
+ return indyFightState;
+}
+
+/////////////////
+// GBAMP
+/////////////////
+
+bool GBAMPAvail = false;
+
+void initGBAMP() {
+ FAT_InitFiles();
+ if (disc_IsInserted()) {
+ GBAMPAvail = true;
+ consolePrintf("Found flash card reader!\n");
+ } else {
+ GBAMPAvail = false;
+ consolePrintf("Flash card reader not found!\n");
+ }
+}
+
+bool isGBAMPAvailable() {
+ return GBAMPAvail;
+}
+
+
+
+/////////////////
+// Main
+/////////////////
+
+static const Common::String test("poo", 1, 16);
+
+
+
+int main(void)
+{
+ soundCallback = NULL;
+
+
+ initHardware();
+
+ // Let arm9 read cartridge
+ *((u16 *) (0x04000204)) &= ~0x0080;
+
+ lastCallbackFrame = 0;
+ tweak = 0;
+
+ indyFightState = false;
+ indyFightRight = true;
+
+ // CPU speed = 67108864
+ // 8 frames = 2946 368.5 bytes per fr
+
+// playSound(stretch, 47619, false);
+// playSound(twang, 11010, true); // 18640
+
+// bufferSize = 10;
+ bufferRate = 22050;
+ bufferFrame = 0;
+// bufferSamples = (bufferRate * bufferSize) / 60;
+ bufferSamples = 4096;
+
+ bufferFirstHalf = false;
+ bufferSecondHalf = true;
+
+ lastEventFrame = 0;
+ mouseMode = MOUSE_LEFT;
+
+
+
+
+ int bytes = (2 * (bufferSamples)) + 100;
+
+ soundBuffer = (s16 *) malloc(bytes * 2);
+
+
+ soundHiPart = true;
+/*
+ TIMER1_CR = 0;
+ TIMER1_DATA = TIMER_FREQ(bufferRate);
+ TIMER1_CR = TIMER_ENABLE | TIMER_DIV_1;
+
+ TIMER2_CR = 0;
+ TIMER2_DATA = 0xFFFF - (bufferSamples / 2);
+ TIMER2_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+ */
+ // 2945 - 2947
+
+
+
+// for (int r = 2946; r < 3000; r++) {
+// soundBuffer[r] = 30000;
+// }
+
+
+
+ consolePrintf("------------------------\n");
+ consolePrintf("ScummVM DS\n");
+ consolePrintf("Ported by Neil Millstone\n");
+#ifdef DS_SCUMM_BUILD
+ consolePrintf("Version 0.61 build A\n");
+#else
+ consolePrintf("Version 0.61 build B\n");
+#endif
+ consolePrintf("------------------------\n");
+ consolePrintf("L/R + D-pad/pen: Scroll view\n");
+ consolePrintf("D-pad left: Left mouse button\n");
+ consolePrintf("D-pad right: Right mouse button\n");
+ consolePrintf("D-pad up: Hover mouse\n");
+ consolePrintf("D-pad down: Skip dialog line\n");
+ consolePrintf("B button: Skip cutscenes\n");
+ consolePrintf("Select: DS Options menu\n");
+ consolePrintf("Start: Game menu\n");
+ consolePrintf("Y (in game): Toggle console\n");
+ consolePrintf("X: Toggle keyboard\n");
+ consolePrintf("A: Swap screens\n");
+ consolePrintf("L + R on bootup: Clear SRAM\n\n");
+ consolePrintf("For a complete poo list see the\n");
+ consolePrintf("help screen.\npoo\n");
+
+
+ for (int r = 0; r < bytes; r++) {
+ soundBuffer[r] = 0;
+ }
+
+ consolePrintf("length=%d str='%s'\n", test.size(), test.c_str());
+
+ swiWaitForVBlank();
+ swiWaitForVBlank();
+ playSound(soundBuffer, (bufferSamples * 2), true);
+ swiWaitForVBlank();
+ swiWaitForVBlank();
+ swiWaitForVBlank();
+
+
+
+ // Create a file system node to force search for a zip file in GBA rom space
+ DSFileSystemNode* node = new DSFileSystemNode();
+ if (!node->getZip() || (!node->getZip()->isReady())) {
+ // If not found, init CF/SD driver
+ initGBAMP();
+ }
+ delete node;
+
+
+
+ updateStatus();
+
+
+// OSystem_DS::instance();
+ g_system = new OSystem_DS();
+ assert(g_system);
+
+ if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) {
+ formatSramOption();
+ }
+
+// printf("'%s'", Common::ConfigManager::kTransientDomain.c_str());
+ //printf("'%s'", Common::ConfigManager::kApplicationDomain.c_str());
+
+
+ char* argv[2] = {"/scummvmds", "--config=scummvmb.ini"};
+#ifdef DS_NON_SCUMM_BUILD
+
+ while (1) {
+ scummvm_main(2, (char **) &argv);
+ }
+#else
+ while (1) {
+ scummvm_main(1, (char **) &argv);
+ }
+#endif
+
+
+ return 0;
+}
+
+}
+
+int main() {
+ DS::main();
+}
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <nds.h>
+
+#include <ARM9/console.h> //basic print funcionality
+
+#include <stdlib.h>
+#include "dsmain.h"
+#include "string.h"
+#include "system.h"
+#include "osystem_ds.h"
+#include "icons_raw.h"
+#include "gba_nds_fat.h"
+#include "disc_io.h"
+#include "config-manager.h"
+#include "engines/scumm/scumm.h"
+#include "keyboard_raw.h"
+#include "keyboard_pal_raw.h"
+#define V16(a, b) ((a << 12) | b)
+#include "touchkeyboard.h"
+#include "registers_alt.h"
+//#include "compact_flash.h"
+#include "dsoptions.h"
+
+namespace DS {
+
+// From console.c in NDSLib
+
+//location of cursor
+extern u8 row;
+extern u8 col;
+
+// Mouse mode
+enum MouseMode {
+ MOUSE_LEFT, MOUSE_RIGHT, MOUSE_HOVER, MOUSE_NUM_MODES
+};
+
+// Defines
+#define FRAME_TIME 17
+#define SCUMM_GAME_HEIGHT 142
+#define SCUMM_GAME_WIDTH 232
+
+int textureID;
+u16* texture;
+
+int frameCount;
+int currentTimeMillis;
+
+// Timer Callback
+int callbackInterval;
+int callbackTimer;
+OSystem::TimerProc callback;
+
+// Scaled
+bool scaledMode;
+int scX;
+int scY;
+
+int subScX;
+int subScY;
+int subScTargetX;
+int subScTargetY;
+int subScreenWidth = SCUMM_GAME_WIDTH;
+int subScreenHeight = SCUMM_GAME_HEIGHT;
+int subScreenScale = 256;
+
+
+
+// Sound
+int bufferSize;
+s16* soundBuffer;
+int bufferFrame;
+int bufferRate;
+int bufferSamples;
+bool soundHiPart;
+
+// Events
+int lastEventFrame;
+bool indyFightState;
+bool indyFightRight;
+
+OSystem::SoundProc soundCallback;
+void* soundParam;
+int lastCallbackFrame;
+bool bufferFirstHalf;
+bool bufferSecondHalf;
+
+// Saved buffers
+u8* savedBuffer = NULL;
+bool highBuffer;
+bool displayModeIs8Bit;
+
+// Game id
+u8 gameID;
+
+bool consoleEnable = true;
+bool gameScreenSwap = false;
+
+MouseMode mouseMode;
+
+// Sprites
+SpriteEntry sprites[128];
+SpriteEntry spritesMain[128];
+int tweak;
+
+// Shake
+int shakePos = 0;
+
+// Keyboard
+bool keyboardEnable = false;
+bool leftHandedMode = false;
+bool keyboardIcon = false;
+
+// Touch
+int touchScX, touchScY, touchX, touchY;
+
+// Dragging
+int dragStartX, dragStartY;
+bool dragging = false;
+int dragScX, dragScY;
+
+// Interface styles
+char gameName[32];
+
+// 8-bit surface size
+int gameWidth = 320;
+int gameHeight = 200;
+
+enum controlType {
+ CONT_SCUMM_ORIGINAL,
+ CONT_SCUMM_SAMNMAX,
+ CONT_SKY,
+ CONT_SIMON,
+};
+
+struct gameListType {
+ char gameId[16];
+ controlType control;
+};
+
+#define NUM_SUPPORTED_GAMES 15
+
+gameListType gameList[NUM_SUPPORTED_GAMES] = {
+ // Unknown game - use normal SCUMM controls
+ {"unknown", CONT_SCUMM_ORIGINAL},
+
+ // SCUMM games
+ {"maniac", CONT_SCUMM_ORIGINAL},
+ {"zak", CONT_SCUMM_ORIGINAL},
+ {"loom", CONT_SCUMM_ORIGINAL},
+ {"indy3", CONT_SCUMM_ORIGINAL},
+ {"atlantis", CONT_SCUMM_ORIGINAL},
+ {"monkey", CONT_SCUMM_ORIGINAL},
+ {"monkey2", CONT_SCUMM_ORIGINAL},
+ {"tentacle", CONT_SCUMM_ORIGINAL},
+ {"samnmax", CONT_SCUMM_SAMNMAX},
+
+ // Non-SCUMM games
+ {"sky", CONT_SKY},
+ {"simon1", CONT_SIMON},
+ {"simon2", CONT_SIMON},
+ {"gob1", CONT_SCUMM_ORIGINAL},
+ {"queen", CONT_SCUMM_ORIGINAL}
+};
+
+gameListType* currentGame = NULL;
+
+// Stylus
+#define ABS(x) ((x)>0?(x):-(x))
+
+bool penDown;
+bool penHeld;
+bool penReleased;
+bool penDownLastFrame;
+f32 penX, penY;
+int keysDownSaved;
+int keysReleasedSaved;
+
+bool penDownSaved;
+bool penReleasedSaved;
+int penDownFrames;
+int touchXOffset = 0;
+int touchYOffset = 0;
+
+u16 savedPalEntry255 = RGB15(31, 31, 31);
+
+
+extern "C" int scummvm_main(int argc, char *argv[]);
+void updateStatus();
+
+TransferSound soundControl;
+
+//plays an 8 bit mono sample at 11025Hz
+void playSound(const void* data, u32 length, bool loop, bool adpcm, int rate)
+{
+
+ if (!IPC->soundData) {
+ soundControl.count = 0;
+ }
+
+ soundControl.data[soundControl.count].data = data;
+ soundControl.data[soundControl.count].len = length | (loop? 0x80000000: 0x00000000);
+ soundControl.data[soundControl.count].rate = rate; // 367 samples per frame
+ soundControl.data[soundControl.count].pan = 64;
+ soundControl.data[soundControl.count].vol = 127;
+ soundControl.data[soundControl.count].format = adpcm? 2: 0;
+
+ soundControl.count++;
+
+ DC_FlushAll();
+ IPC->soundData = &soundControl;
+}
+
+void stopSound(int channel) {
+ playSound(NULL, 0, false, false, -channel);
+}
+
+void updateOAM() {
+ DC_FlushAll();
+ dmaCopy(sprites, OAM_SUB, 128 * sizeof(SpriteEntry));
+ dmaCopy(spritesMain, OAM, 128 * sizeof(SpriteEntry));
+}
+
+void setGameSize(int width, int height) {
+ gameWidth = width;
+ gameHeight = height;
+}
+
+int getGameWidth() {
+ return gameWidth;
+}
+
+int getGameHeight() {
+ return gameHeight;
+}
+
+void initSprites() {
+ for(int i = 0; i < 128; i++) {
+ sprites[i].attribute[0] = ATTR0_DISABLED;
+ sprites[i].attribute[1] = 0;
+ sprites[i].attribute[2] = 0;
+ sprites[i].attribute[3] = 0;
+ }
+
+ for(int i = 0; i < 128; i++) {
+ spritesMain[i].attribute[0] = ATTR0_DISABLED;
+ spritesMain[i].attribute[1] = 0;
+ spritesMain[i].attribute[2] = 0;
+ spritesMain[i].attribute[3] = 0;
+ }
+
+ updateOAM();
+}
+
+
+void saveGameBackBuffer() {
+#ifdef DISABLE_SCUMM
+ if (savedBuffer == NULL) savedBuffer = new u8[gameWidth * gameHeight];
+ for (int r = 0; r < 200; r++) {
+ memcpy(savedBuffer + (r * gameWidth), ((u8 *) (get8BitBackBuffer())) + (r * 512), gameWidth);
+ }
+#endif
+}
+
+void restoreGameBackBuffer() {
+#ifdef DISABLE_SCUMM
+ if (savedBuffer) {
+ for (int r = 0; r < 200; r++) {
+ memcpy(((u8 *) (BG_GFX_SUB)) + (r * 512), savedBuffer + (r * gameWidth), gameWidth);
+ memcpy(((u8 *) (get8BitBackBuffer())) + (r * 512), savedBuffer + (r * gameWidth), gameWidth);
+ }
+ delete savedBuffer;
+ savedBuffer = NULL;
+ }
+#endif
+
+#ifndef DISABLE_SCUMM
+ memset(get8BitBackBuffer(), 0, 512 * 256);
+ memset(BG_GFX_SUB, 0, 512 * 256);
+ if (Scumm::g_scumm) {
+ Scumm::g_scumm->markRectAsDirty(Scumm::kMainVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1);
+ Scumm::g_scumm->markRectAsDirty(Scumm::kTextVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1);
+ Scumm::g_scumm->markRectAsDirty(Scumm::kVerbVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1);
+ }
+#endif
+
+}
+
+
+void initGame() {
+ // This is a good time to check for left handed mode since the mode change is done as the game starts.
+ // There's probably a better way, but hey.
+// consolePrintf("initing game\n");
+
+ setOptions();
+
+ //strcpy(gameName, ConfMan.getActiveDomain().c_str());
+ strcpy(gameName, ConfMan.get("gameid").c_str());
+// consolePrintf("\n\n\n\nCurrent game: '%s' %d\n", gameName, gameName[0]);
+
+ currentGame = &gameList[0]; // Default game
+
+ for (int r = 0; r < NUM_SUPPORTED_GAMES; r++) {
+ if (!stricmp(gameName, gameList[r].gameId)) {
+ currentGame = &gameList[r];
+// consolePrintf("Game list num: %d\n", currentGame);
+ }
+ }
+
+
+}
+
+void setLeftHanded(bool enable) {
+ leftHandedMode = enable;
+}
+
+void setTouchXOffset(int x) {
+ touchXOffset = x;
+}
+
+void setTouchYOffset(int y) {
+ touchYOffset = y;
+}
+
+void setUnscaledMode(bool enable) {
+ scaledMode = !enable;
+}
+
+void displayMode8Bit() {
+
+ u16 buffer[32 * 32];
+
+ setKeyboardEnable(false);
+
+ if (!displayModeIs8Bit) {
+ for (int r = 0; r < 32 * 32; r++) {
+ buffer[r] = ((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r];
+ }
+ }
+
+
+
+ videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);
+ videoSetModeSub(MODE_3_2D /*| DISPLAY_BG0_ACTIVE*/ | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text
+
+ vramSetBankA(VRAM_A_MAIN_BG_0x6000000);
+ vramSetBankB(VRAM_B_MAIN_BG_0x6020000);
+
+ vramSetBankC(VRAM_C_SUB_BG_0x6200000);
+ vramSetBankD(VRAM_D_MAIN_BG_0x6040000);
+
+ vramSetBankH(VRAM_H_LCD);
+
+ BG3_CR = BG_BMP8_512x256 | BG_BMP_BASE(8);
+
+
+
+ BG3_XDX = (int) (((float) (gameWidth) / 256.0f) * 256);
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = (int) ((200.0f / 192.0f) * 256);
+
+ SUB_BG3_CR = BG_BMP8_512x256;
+
+
+
+ SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256);
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256);
+
+
+ // Do text stuff
+ BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1);
+ BG0_Y0 = 0;
+
+ // Restore palette entry used by text in the front-end
+ PALETTE_SUB[255] = savedPalEntry255;
+
+ consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16);
+ consolePrintSet(0, 23);
+
+ if (!displayModeIs8Bit) {
+ for (int r = 0; r < 32 * 32; r++) {
+ ((u16 *) SCREEN_BASE_BLOCK(0))[r] = buffer[r];
+ }
+// dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK(0), buffer, 32 * 32 * 2);
+ }
+
+
+ if (!displayModeIs8Bit) restoreGameBackBuffer();
+ displayModeIs8Bit = true;
+
+ POWER_CR &= ~POWER_SWAP_LCDS;
+
+ keyboardEnable = false;
+ initGame();
+
+}
+
+void setGameID(int id) {
+ gameID = id;
+}
+
+void dummyHandler() {
+ REG_IF = IRQ_VBLANK;
+}
+
+void checkSleepMode() {
+ if (IPC->performArm9SleepMode) {
+
+ consolePrintf("ARM9 Entering sleep mode\n");
+
+ int intSave = REG_IE;
+ irqSet(IRQ_VBLANK, dummyHandler);
+// int irqHandlerSave = (int) IRQ_HANDLER;
+ REG_IE = IRQ_VBLANK;
+ //IRQ_HANDLER = dummyHandler;
+
+ int powerSave = POWER_CR;
+ POWER_CR &= ~POWER_ALL;
+
+ while (IPC->performArm9SleepMode) {
+ swiWaitForVBlank();
+ }
+
+ POWER_CR = powerSave;
+// IRQ_HANDLER = (void (*)()) irqHandlerSave;
+ irqSet(IRQ_VBLANK, VBlankHandler);
+ REG_IE = intSave;
+
+ consolePrintf("ARM9 Waking from sleep mode\n");
+ }
+}
+
+void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor) {
+ if (currentGame->control != CONT_SCUMM_SAMNMAX)
+ return;
+
+ uint16 border = RGB15(24,24,24) | 0x8000;
+
+
+ int off = 48*64;
+ memset(SPRITE_GFX_SUB+off, 0, 64*64*2);
+
+ int pos = 190 - (w+2);
+
+
+
+ // make border
+ for (uint i=0; i<w+2; i++) {
+ SPRITE_GFX_SUB[off+i] = border;
+ SPRITE_GFX_SUB[off+(31)*64+i] = border;
+ }
+ for (uint i=1; i<31; i++) {
+ SPRITE_GFX_SUB[off+(i*64)] = border;
+ SPRITE_GFX_SUB[off+(i*64)+(w+1)] = border;
+ }
+
+ int offset = (32 - h) >> 1;
+
+ for (uint y=0; y<h; y++) {
+ for (uint x=0; x<w; x++) {
+ int color = icon[y*w+x];
+ if (color == keycolor) {
+ SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = 0x8000; // black background
+ } else {
+ SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = BG_PALETTE[color] | 0x8000;
+ }
+ }
+ }
+
+ sprites[1].attribute[0] = ATTR0_BMP | 150;
+ sprites[1].attribute[1] = ATTR1_SIZE_64 | pos;
+ sprites[1].attribute[2] = ATTR2_ALPHA(1) | 48;
+}
+
+
+
+
+void displayMode16Bit() {
+
+ u16 buffer[32 * 32 * 2];
+
+
+ if (displayModeIs8Bit) {
+ saveGameBackBuffer();
+ for (int r = 0; r < 32 * 32; r++) {
+ buffer[r] = ((u16 *) SCREEN_BASE_BLOCK(0))[r];
+ }
+ }
+
+
+ videoSetMode(MODE_5_2D | /*DISPLAY_BG0_ACTIVE |*/ DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);
+ videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE |/* DISPLAY_BG1_ACTIVE |*/ DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text
+
+ vramSetBankA(VRAM_A_MAIN_BG);
+ vramSetBankB(VRAM_B_MAIN_BG);
+ vramSetBankC(VRAM_C_MAIN_BG);
+ vramSetBankD(VRAM_D_MAIN_BG);
+ vramSetBankH(VRAM_H_SUB_BG);
+
+ BG3_CR = BG_BMP16_512x256;
+ highBuffer = false;
+
+ BG3_XDX = (int) (1.25f * 256);
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = (int) ((200.0f / 192.0f) * 256);
+
+ memset(BG_GFX, 0, 512 * 256 * 2);
+
+ savedPalEntry255 = PALETTE_SUB[255];
+ PALETTE_SUB[255] = RGB15(31,31,31);//by default font will be rendered with color 255
+
+ // Do text stuff
+ SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0);
+ SUB_BG0_Y0 = 0;
+
+ consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);
+
+ if (displayModeIs8Bit) {
+ //dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK_SUB(0), buffer, 32 * 32 * 2);
+ for (int r = 0; r < 32 * 32; r++) {
+ ((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r] = buffer[r];
+ }
+ }
+
+ consolePrintSet(0, 23);
+ consolePrintf("\n");
+
+ // Show keyboard
+ SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12);
+ //drawKeyboard(1, 12);
+
+ POWER_CR &= ~POWER_SWAP_LCDS;
+
+
+
+ displayModeIs8Bit = false;
+}
+
+
+void displayMode16BitFlipBuffer() {
+ if (!displayModeIs8Bit) {
+ u16* back = get16BitBackBuffer();
+
+// highBuffer = !highBuffer;
+// BG3_CR = BG_BMP16_512x256 | BG_BMP_RAM(highBuffer? 1: 0);
+
+ for (int r = 0; r < 512 * 256; r++) {
+ *(BG_GFX + r) = *(back + r);
+ }
+ }
+}
+
+void setShakePos(int shakePos) {
+ shakePos = shakePos;
+}
+
+
+u16* get16BitBackBuffer() {
+ return BG_GFX + 0x20000;
+}
+
+u16* get8BitBackBuffer() {
+ return BG_GFX + 0x10000; // 16bit qty!
+}
+
+void setSoundProc(OSystem::SoundProc proc, void* param) {
+// consolePrintf("Set sound callback");
+ soundCallback = proc;
+ soundParam = param;
+}
+
+// The sound system in ScummVM seems to always return stereo interleaved samples.
+// Here, I'm treating an 11Khz stereo stream as a 22Khz mono stream, which works sorta ok, but is
+// a horrible bodge. Any advice on how to change the engine to output mono would be greatly
+// appreciated.
+void doSoundCallback() {
+ if ((soundCallback)) {
+ lastCallbackFrame = frameCount;
+
+ for (int r = IPC->playingSection; r < IPC->playingSection + 4; r++) {
+ int chunk = r & 3;
+
+ if (IPC->fillNeeded[chunk]) {
+ IPC->fillNeeded[chunk] = false;
+ DC_FlushAll();
+ soundCallback(soundParam, (byte *) (soundBuffer + ((bufferSamples >> 2) * chunk)), bufferSamples >> 1);
+ IPC->fillNeeded[chunk] = false;
+ DC_FlushAll();
+ }
+
+ }
+
+ }
+}
+
+void doTimerCallback() {
+ if (callback) {
+ if (callbackTimer <= 0) {
+ callbackTimer += callbackInterval;
+ callback(callbackInterval);
+ }
+ }
+}
+
+void soundUpdate() {
+ if ((bufferFrame == 0)) {
+// playSound(soundBuffer, (bufferSamples * 2), true);
+ }
+// consolePrintf("%x\n", IPC->test);
+
+
+ if (bufferFrame == 0) {
+// bufferFirstHalf = true;
+ }
+ if (bufferFrame == bufferSize >> 1) {
+ //bufferSecondHalf = true;
+ }
+
+ bufferFrame++;
+ if (bufferFrame == bufferSize) {
+ bufferFrame = 0;
+ }
+}
+
+void memoryReport() {
+ int r = 0;
+ int* p;
+ do {
+ p = (int *) malloc(r * 8192);
+ free(p);
+ r++;
+ } while ((p) && (r < 512));
+
+ int t = -1;
+ void* block[1024];
+ do {
+ t++;
+ block[t] = (int *) malloc(4096);
+ } while ((t < 1024) && (block[t]));
+
+ for (int q = 0; q < t; q++) {
+ free(block[q]);
+ }
+
+ consolePrintf("Free: %dK, Largest: %dK\n", t * 4, r * 8);
+}
+
+
+void addIndyFightingKeys() {
+ OSystem_DS* system = OSystem_DS::instance();
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.flags = 0;
+
+ if ((getKeysDown() & KEY_L)) {
+ indyFightRight = false;
+ }
+
+ if ((getKeysDown() & KEY_R)) {
+ indyFightRight = true;
+ }
+
+ if ((getKeysDown() & KEY_UP)) {
+ event.kbd.keycode = '8';
+ event.kbd.ascii = '8';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_LEFT)) {
+ event.kbd.keycode = '4';
+ event.kbd.ascii = '4';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_RIGHT)) {
+ event.kbd.keycode = '6';
+ event.kbd.ascii = '6';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_DOWN)) {
+ event.kbd.keycode = '2';
+ event.kbd.ascii = '2';
+ system->addEvent(event);
+ }
+
+ if (indyFightRight) {
+
+ if ((getKeysDown() & KEY_X)) {
+ event.kbd.keycode = '9';
+ event.kbd.ascii = '9';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_A)) {
+ event.kbd.keycode = '6';
+ event.kbd.ascii = '6';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_B)) {
+ event.kbd.keycode = '3';
+ event.kbd.ascii = '3';
+ system->addEvent(event);
+ }
+
+ } else {
+
+ if ((getKeysDown() & KEY_X)) {
+ event.kbd.keycode = '7';
+ event.kbd.ascii = '7';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_A)) {
+ event.kbd.keycode = '4';
+ event.kbd.ascii = '4';
+ system->addEvent(event);
+ }
+ if ((getKeysDown() & KEY_B)) {
+ event.kbd.keycode = '1';
+ event.kbd.ascii = '1';
+ system->addEvent(event);
+ }
+
+ }
+
+
+ if ((getKeysDown() & KEY_Y)) {
+ event.kbd.keycode = '5';
+ event.kbd.ascii = '5';
+ system->addEvent(event);
+ }
+}
+
+
+void setKeyboardEnable(bool en) {
+ if (en == keyboardEnable) return;
+ keyboardEnable = en;
+ u16* backupBank = (u16 *) 0x6040000;
+
+ if (keyboardEnable) {
+
+
+ DS::drawKeyboard(1, 12, backupBank);
+
+
+ SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12);
+
+ if (displayModeIs8Bit) {
+ SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer
+ SUB_DISPLAY_CR &= ~DISPLAY_BG3_ACTIVE; // Turn off game layer
+ } else {
+ SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer
+ SUB_DISPLAY_CR &= ~DISPLAY_BG0_ACTIVE; // Turn off console layer
+ }
+ lcdSwap();
+ } else {
+
+
+ // Restore the palette that the keyboard has used
+ for (int r = 0; r < 256; r++) {
+ BG_PALETTE_SUB[r] = BG_PALETTE[r];
+ }
+
+
+ //restoreVRAM(1, 12, backupBank);
+
+ if (displayModeIs8Bit) {
+ // Copy the sub screen VRAM from the top screen - they should always be
+ // the same.
+ for (int r = 0; r < (512 * 256) >> 1; r++) {
+ BG_GFX_SUB[r] = get8BitBackBuffer()[r];
+ }
+
+ SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer
+ SUB_DISPLAY_CR |= DISPLAY_BG3_ACTIVE; // Turn on game layer
+ } else {
+ SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer
+ SUB_DISPLAY_CR |= DISPLAY_BG0_ACTIVE; // Turn on console layer
+ }
+
+ lcdSwap();
+ }
+}
+
+bool getKeyboardEnable() {
+ return keyboardEnable;
+}
+
+bool getIsDisplayMode8Bit() {
+ return displayModeIs8Bit;
+}
+
+void addEventsToQueue() {
+ OSystem_DS* system = OSystem_DS::instance();
+ OSystem::Event event;
+
+
+
+
+ if (system->isEventQueueEmpty()) {
+
+/*
+ if (getKeysDown() & KEY_L) {
+ tweak--;
+ consolePrintf("Tweak: %d\n", tweak);
+ IPC->tweakChanged = true;
+ }
+
+
+ if (getKeysDown() & KEY_R) {
+ tweak++;
+ consolePrintf("Tweak: %d\n", tweak);
+ IPC->tweakChanged = true;
+ }
+ */
+ if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) {
+ memoryReport();
+ }
+
+ if (displayModeIs8Bit) {
+
+ if (!indyFightState) {
+
+
+ if ((getKeysDown() & KEY_B) && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) {
+ // consolePrintf("Pressing Esc");
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 27;
+ event.kbd.ascii = 27;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+ }
+
+ }
+
+
+
+ if ((!getIndyFightState()) && (getKeysDown() & KEY_Y)) {
+ consoleEnable = !consoleEnable;
+ if (displayModeIs8Bit) {
+ displayMode8Bit();
+ } else {
+ displayMode16Bit();
+ }
+ }
+
+
+ if (!((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (!getIndyFightState())) {
+
+ if ((getKeysDown() & KEY_A) && (!indyFightState)) {
+ gameScreenSwap = !gameScreenSwap;
+ }
+
+ if (!getPenHeld() || (mouseMode != MOUSE_HOVER)) {
+ if (getKeysDown() & KEY_LEFT) {
+ mouseMode = MOUSE_LEFT;
+ }
+ if (getKeysDown() & KEY_RIGHT) {
+ if (currentGame->control != CONT_SCUMM_SAMNMAX) {
+ mouseMode = MOUSE_RIGHT;
+ } else {
+ // If we're playing sam and max, click and release the right mouse
+ // button to change verb
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_RBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_RBUTTONUP;
+ system->addEvent(event);
+ }
+ }
+ if (getKeysDown() & KEY_UP) {
+ mouseMode = MOUSE_HOVER;
+ }
+ }
+
+
+
+ }
+
+ if ((getKeysDown() & KEY_SELECT)) {
+ //scaledMode = !scaledMode;
+ //scY = 4;
+ showOptionsDialog();
+ }
+
+
+ }
+
+ if (!getIndyFightState() && !((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (getKeysDown() & KEY_X)) {
+ setKeyboardEnable(!keyboardEnable);
+ }
+
+ updateStatus();
+
+ OSystem::Event event;
+
+ if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) {
+ event.type = OSystem::EVENT_MOUSEMOVE;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ //consolePrintf("x=%d y=%d \n", getPenX(), getPenY());
+ }
+
+ if (!keyboardEnable) {
+ if ((mouseMode != MOUSE_HOVER) || (!displayModeIs8Bit)) {
+ if (getPenDown() && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) {
+ event.type = ((mouseMode == MOUSE_LEFT) || (!displayModeIs8Bit))? OSystem::EVENT_LBUTTONDOWN: OSystem::EVENT_RBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+
+ if (getPenReleased()) {
+ event.type = mouseMode == MOUSE_LEFT? OSystem::EVENT_LBUTTONUP: OSystem::EVENT_RBUTTONUP;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+ } else {
+ // In hover mode, D-pad left and right click the mouse when the pen is on the screen
+
+ if (getPenHeld()) {
+ if (getKeysDown() & KEY_LEFT) {
+ event.type = OSystem::EVENT_LBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+ /* if (getKeysReleased() & KEY_LEFT) {
+ event.type = OSystem::EVENT_LBUTTONUP;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }*/
+
+ if (getKeysDown() & KEY_RIGHT) {
+ event.type = OSystem::EVENT_RBUTTONDOWN;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }
+ /*if (getKeysReleased() & KEY_RIGHT) {
+ event.type = OSystem::EVENT_RBUTTONUP;
+ event.mouse = Common::Point(getPenX(), getPenY());
+ system->addEvent(event);
+ }*/
+ }
+ }
+
+ if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R)) && (displayModeIs8Bit)) {
+ // Controls specific to the control method
+
+
+ if (currentGame->control == CONT_SKY) {
+ // Extra controls for Benieth a Steel Sky
+ if ((getKeysDown() & KEY_DOWN)) {
+ penY = 0;
+ penX = 0; // Show inventory by moving mouse onto top line
+ }
+ }
+
+ if (currentGame->control == CONT_SIMON) {
+ // Extra controls for Simon the Sorcerer
+ if ((getKeysDown() & KEY_DOWN)) {
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = '#'; // F10 or # - show hotspots
+ event.kbd.ascii = '#';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+// consolePrintf("F10\n");
+ }
+ }
+
+ if (currentGame->control == CONT_SCUMM_ORIGINAL) {
+ // Extra controls for Scumm v1-5 games
+ if ((getKeysDown() & KEY_DOWN)) {
+ OSystem::Event event;
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = '.'; // Full stop - skips current dialogue line
+ event.kbd.ascii = '.';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+ }
+
+ if (indyFightState) {
+ addIndyFightingKeys();
+ }
+
+ }
+
+ }
+ }
+
+ if (!displayModeIs8Bit) {
+ // Front end controls
+
+ if (leftHandedSwap(getKeysDown()) & KEY_UP) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = SDLK_UP;
+ event.kbd.ascii = 0;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+ }
+
+ if (leftHandedSwap(getKeysDown()) & KEY_DOWN) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = SDLK_DOWN;
+ event.kbd.ascii = 0;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+ }
+
+ if (leftHandedSwap(getKeysDown()) & KEY_A) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = SDLK_RETURN;
+ event.kbd.ascii = 0;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+ }
+
+ }
+
+
+ if ((getKeysDown() & KEY_START)) {
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 319; // F5
+ event.kbd.ascii = 319;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+/*
+ event.type = OSystem::EVENT_KEYUP;
+ event.kbd.keycode = 319; // F5
+ event.kbd.ascii = 319;
+ system->addEvent(event);*/
+
+// consolePrintf("Pressing F5");
+ }
+
+
+ if (keyboardEnable) {
+ DS::addKeyboardEvents();
+ }
+
+ consumeKeys();
+
+ consumePenEvents();
+
+ }
+}
+
+void updateStatus() {
+ int offs;
+
+ if (displayModeIs8Bit) {
+ switch (mouseMode) {
+ case MOUSE_LEFT: {
+ offs = 16;
+ break;
+ }
+ case MOUSE_RIGHT: {
+ offs = 32;
+ break;
+ }
+ case MOUSE_HOVER: {
+ offs = 0;
+ break;
+ }
+ default: {
+ // Nothing!
+ offs = 0;
+ break;
+ }
+ }
+
+
+ sprites[0].attribute[0] = ATTR0_BMP | 150;
+ sprites[0].attribute[1] = ATTR1_SIZE_32 | 208;
+ sprites[0].attribute[2] = ATTR2_ALPHA(1)| offs;
+
+ if (indyFightState) {
+ sprites[2].attribute[0] = ATTR0_BMP | 150;
+ sprites[2].attribute[1] = ATTR1_SIZE_32 | (190 - 32) | (indyFightRight? 0: ATTR1_FLIP_X);
+ sprites[2].attribute[2] = ATTR2_ALPHA(1)| 48;
+ } else {
+ sprites[2].attribute[0] = ATTR0_DISABLED;
+ sprites[2].attribute[1] = 0;
+ sprites[2].attribute[2] = 0;
+ }
+ } else {
+ sprites[0].attribute[0] = ATTR0_DISABLED;
+ sprites[1].attribute[0] = ATTR0_DISABLED;
+ sprites[2].attribute[0] = ATTR0_DISABLED;
+ sprites[3].attribute[0] = ATTR0_DISABLED;
+ }
+
+ if ((keyboardIcon) && (!keyboardEnable) && (!displayModeIs8Bit)) {
+ spritesMain[0].attribute[0] = ATTR0_BMP | 160;
+ spritesMain[0].attribute[1] = ATTR1_SIZE_32 | 0;
+ spritesMain[0].attribute[2] = ATTR2_ALPHA(1) | 64;
+ } else {
+ spritesMain[0].attribute[0] = ATTR0_DISABLED;
+ spritesMain[0].attribute[1] = 0;
+ spritesMain[0].attribute[2] = 0;
+ spritesMain[0].attribute[3] = 0;
+ }
+
+}
+
+void soundBufferEmptyHandler() {
+ REG_IF = IRQ_TIMER2;
+
+ if (soundHiPart) {
+// bufferSecondHalf = true;
+ } else {
+// bufferFirstHalf = true;
+ }
+
+ soundHiPart = !soundHiPart;
+}
+
+void setMainScreenScroll(int x, int y) {
+ if (gameScreenSwap) {
+ SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ SUB_BG3_CY = y;
+ } else {
+ BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ BG3_CY = y;
+
+ touchX = x >> 8;
+ touchY = y >> 8;
+ }
+}
+
+void setMainScreenScale(int x, int y) {
+ if (gameScreenSwap) {
+ SUB_BG3_XDX = x;
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = y;
+ } else {
+ BG3_XDX = x;
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = y;
+
+ touchScX = x;
+ touchScY = y;
+ }
+}
+
+void setZoomedScreenScroll(int x, int y) {
+ if (gameScreenSwap) {
+ BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ BG3_CY = y;
+
+ touchX = x >> 8;
+ touchY = y >> 8;
+ } else {
+ SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0);
+ SUB_BG3_CY = y;
+ }
+}
+
+void setZoomedScreenScale(int x, int y) {
+ if (gameScreenSwap) {
+ BG3_XDX = x;
+ BG3_XDY = 0;
+ BG3_YDX = 0;
+ BG3_YDY = y;
+
+ touchScX = x;
+ touchScY = y;
+ } else {
+ SUB_BG3_XDX = x;
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = y;
+ }
+}
+
+void VBlankHandler(void) {
+// BG_PALETTE[0] = RGB15(31, 31, 31);
+// if (*((int *) (0x023FFF00)) != 0xBEEFCAFE) {
+ // consolePrintf("Guard band overwritten!");
+// }
+
+// consolePri ntf("X:%d Y:%d\n", getPenX(), getPenY());
+
+
+ IPC->tweak = tweak;
+ soundUpdate();
+
+
+
+
+ if ((!gameScreenSwap) && (!(getKeysHeld() & KEY_L) && !(getKeysHeld() & KEY_R))) {
+ if (currentGame) {
+ if (currentGame->control != CONT_SCUMM_SAMNMAX) {
+ if (getPenHeld() && (getPenY() < SCUMM_GAME_HEIGHT)) {
+ setTopScreenTarget(getPenX(), getPenY());
+ }
+ } else {
+ if (getPenHeld()) {
+ setTopScreenTarget(getPenX(), getPenY());
+ }
+ }
+ }
+ }
+
+
+ penUpdate();
+ keysUpdate();
+
+
+ frameCount++;
+
+
+
+ if (callback) {
+ callbackTimer -= FRAME_TIME;
+ }
+
+ if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) {
+
+ if ((!dragging) && (getPenHeld()) && (penDownFrames > 5)) {
+ dragging = true;
+ dragStartX = penX;
+ dragStartY = penY;
+
+ if (gameScreenSwap) {
+ dragScX = subScTargetX;
+ dragScY = subScTargetY;
+ } else {
+ dragScX = scX;
+ dragScY = scY;
+ }
+
+
+ }
+
+ if ((dragging) && (!getPenHeld())) {
+ dragging = false;
+ }
+
+ if (dragging) {
+
+ if (gameScreenSwap) {
+ subScTargetX = dragScX + ((dragStartX - penX) << 8);
+ subScTargetY = dragScY + ((dragStartY - penY) << 8);
+ } else {
+ scX = dragScX + ((dragStartX - penX));
+ scY = dragScY + ((dragStartY - penY));
+ }
+
+// consolePrintf("X:%d Y:%d\n", dragStartX - penX, dragStartY - penY);
+ }
+ }
+
+
+/* if ((frameCount & 1) == 0) {
+ SUB_BG3_CX = subScX;
+ } else {
+ SUB_BG3_CX = subScX + 64;
+ }
+
+ SUB_BG3_CY = subScY + (shakePos << 8);*/
+
+ /*SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256);
+ SUB_BG3_XDY = 0;
+ SUB_BG3_YDX = 0;
+ SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256);*/
+
+
+ if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) {
+ if ((getKeysHeld() & KEY_A) && (subScreenScale < 256)) {
+ subScreenScale += 3;
+ }
+
+ if ((getKeysHeld() & KEY_B) && (subScreenScale > 128)) {
+ subScreenScale -=3;
+ }
+
+ int xCenter = subScTargetX + ((subScreenWidth >> 1) << 8);
+ int yCenter = subScTargetY + ((subScreenHeight >> 1) << 8);
+
+ subScreenWidth = SCUMM_GAME_WIDTH * subScreenScale >> 8;
+ subScreenHeight = SCUMM_GAME_HEIGHT * subScreenScale >> 8;
+
+ subScTargetX = xCenter - ((subScreenWidth >> 1) << 8);
+ subScTargetY = yCenter - ((subScreenHeight >> 1) << 8);
+
+
+
+
+ if (subScTargetX < 0) subScTargetX = 0;
+ if (subScTargetX > (gameWidth - subScreenWidth) << 8) subScTargetX = (gameWidth - subScreenWidth) << 8;
+
+ if (subScTargetY < 0) subScTargetY = 0;
+ if (subScTargetY > (gameHeight - subScreenHeight) << 8) subScTargetY = (gameHeight - subScreenHeight) << 8;
+ }
+
+ subScX += (subScTargetX - subScX) >> 2;
+ subScY += (subScTargetY - subScY) >> 2;
+
+ if (displayModeIs8Bit) {
+
+ if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) {
+
+ int offsX = 0, offsY = 0;
+
+
+ if (getKeysHeld() & KEY_LEFT) {
+ offsX -= 1;
+ }
+
+ if (getKeysHeld() & KEY_RIGHT) {
+ offsX += 1;
+ }
+
+ if (getKeysHeld() & KEY_UP) {
+ offsY -= 1;
+ }
+
+ if (getKeysHeld() & KEY_DOWN) {
+ offsY += 1;
+ }
+
+ if (((gameScreenSwap) && (getKeysHeld() & KEY_L)) || ((!gameScreenSwap) && (getKeysHeld() & KEY_R))) {
+ subScTargetX += offsX << 8;
+ subScTargetY += offsY << 8;
+ } else {
+ scX += offsX;
+ scY += offsY;
+ }
+ }
+
+ if (!scaledMode) {
+
+ if (scX + 256 > gameWidth - 1) {
+ scX = gameWidth - 1 - 256;
+ }
+
+ if (scX < 0) {
+ scX = 0;
+ }
+
+ if (scY + 192 > gameHeight - 1) {
+ scY = gameHeight - 1 - 192;
+ }
+
+ if (scY < 0) {
+ scY = 0;
+ }
+
+ setZoomedScreenScroll(subScX, subScY);
+ setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192);
+
+
+ setMainScreenScroll(scX << 8, (scY << 8) + (shakePos << 8));
+ setMainScreenScale(256, 256); // 1:1 scale
+
+ } else {
+
+ if (scY > gameHeight - 192 - 1) {
+ scY = gameHeight - 192 - 1;
+ }
+
+ if (scY < 0) {
+ scY = 0;
+ }
+
+ setZoomedScreenScroll(subScX, subScY);
+ setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192);
+
+ setMainScreenScroll(64, (scY << 8) + (shakePos << 8));
+ setMainScreenScale(320, 256); // 1:1 scale
+
+ }
+ } else {
+ setZoomedScreenScroll(0, 0);
+ setZoomedScreenScale(320, 256);
+
+ setMainScreenScroll(0, 0);
+ setMainScreenScale(320, 256); // 1:1 scale
+ }
+
+ // Enable on screen keyboard when pen taps icon
+ if ((keyboardIcon) && (penX < 32) && (penY > 160) && (penHeld)) {
+ setKeyboardEnable(true);
+ }
+
+ if (keyboardEnable) {
+ if (DS::getKeyboardClosed()) {
+ setKeyboardEnable(false);
+ }
+ }
+
+ updateOAM();
+
+ //PALETTE[0] = RGB15(0, 0, 0);
+ REG_IF = IRQ_VBLANK;
+}
+
+int getMillis() {
+ return currentTimeMillis;
+// return frameCount * FRAME_TIME;
+}
+
+void setTimerCallback(OSystem::TimerProc proc, int interval) {
+// consolePrintf("Set timer proc %x, int %d\n", proc, interval);
+ callback = proc;
+ callbackInterval = interval;
+ callbackTimer = interval;
+}
+
+void timerTickHandler() {
+ REG_IF = IRQ_TIMER0;
+ if ((callback) && (callbackTimer > 0)) {
+ callbackTimer--;
+ }
+ currentTimeMillis++;
+}
+
+void setTalkPos(int x, int y) {
+// if (gameID != Scumm::GID_SAMNMAX) {
+// setTopScreenTarget(x, 0);
+// } else {
+ setTopScreenTarget(x, y);
+// }
+}
+
+void setTopScreenTarget(int x, int y) {
+ subScTargetX = (x - (subScreenWidth >> 1));
+ subScTargetY = (y - (subScreenHeight >> 1));
+
+ if (subScTargetX < 0) subScTargetX = 0;
+ if (subScTargetX > gameWidth - subScreenWidth) subScTargetX = gameWidth - subScreenWidth;
+
+ if (subScTargetY < 0) subScTargetY = 0;
+ if (subScTargetY > gameHeight - subScreenHeight) subScTargetY = gameHeight - subScreenHeight;
+
+ subScTargetX <<=8;
+ subScTargetY <<=8;
+}
+
+void initHardware() {
+ // Guard band
+//((int *) (0x023FFF00)) = 0xBEEFCAFE;
+
+
+ penInit();
+
+ powerON(POWER_ALL);
+/* vramSetBankA(VRAM_A_MAIN_BG);
+ vramSetBankB(VRAM_B_MAIN_BG);
+ vramSetBankC(VRAM_C_SUB_BG); */
+ vramSetBankI(VRAM_I_SUB_SPRITE);
+ vramSetBankG(VRAM_G_MAIN_SPRITE);
+
+ currentTimeMillis = 0;
+
+
+/*
+ // Set up a millisecond counter
+ TIMER0_CR = 0;
+ TIMER0_DATA = 0xFFFF;
+ TIMER0_CR = TIMER_ENABLE | TIMER_CASCADE;
+*/
+
+
+
+ PALETTE[255] = RGB15(0,31,0);
+
+ // Allocate save buffer for game screen
+// savedBuffer = new u8[320 * 200];
+ displayMode16Bit();
+
+ memset(BG_GFX, 0, 512 * 256 * 2);
+ scaledMode = true;
+ scX = 0;
+ scY = 0;
+ subScX = 0;
+ subScY = 0;
+ subScTargetX = 0;
+ subScTargetY = 0;
+
+ //lcdSwap();
+ POWER_CR &= ~POWER_SWAP_LCDS;
+
+ frameCount = 0;
+ callback = NULL;
+
+// vramSetBankH(VRAM_H_SUB_BG);
+
+
+// // Do text stuff
+ //BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1);
+// BG0_Y0 = 48;
+
+ PALETTE[255] = RGB15(31,31,31);//by default font will be rendered with color 255
+
+ //consoleInit() is a lot more flexible but this gets you up and running quick
+// consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16);
+ //consolePrintSet(0, 6);
+
+ //irqs are nice
+ irqInit();
+// irqInitHandler();
+ irqSet(IRQ_VBLANK, VBlankHandler);
+ irqSet(IRQ_TIMER0, timerTickHandler);
+ irqSet(IRQ_TIMER2, soundBufferEmptyHandler);
+
+ irqEnable(IRQ_VBLANK);
+ irqEnable(IRQ_TIMER0);
+ irqEnable(IRQ_TIMER2);
+
+
+ // Set up a millisecond timer
+ TIMER0_CR = 0;
+ TIMER0_DATA = (u32) TIMER_FREQ(1000);
+ TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1 | TIMER_IRQ_REQ;
+ REG_IME = 1;
+
+ PALETTE[255] = RGB15(0,0,31);
+
+ initSprites();
+
+// videoSetModeSub(MODE_3_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text
+
+ // Convert texture from 24bit 888 to 16bit 1555, remembering to set top bit!
+ u8* srcTex = (u8 *) icons_raw;
+ for (int r = 32 * 160 ; r >= 0; r--) {
+ SPRITE_GFX_SUB[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10);
+ SPRITE_GFX[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10);
+ }
+
+ WAIT_CR &= ~(0x0080);
+ REG_WRAM_CNT = 0;
+
+}
+
+
+void setKeyboardIcon(bool enable) {
+ keyboardIcon = enable;
+}
+
+bool getKeyboardIcon() {
+ return keyboardIcon;
+}
+
+
+////////////////////
+// Pen stuff
+////////////////////
+
+
+void penInit() {
+ penDown = false;
+ penHeld = false;
+ penReleased = false;
+ penDownLastFrame = false;
+ penDownSaved = false;
+ penReleasedSaved = false;
+ penDownFrames = 0;
+ consumeKeys();
+}
+
+void penUpdate() {
+
+// if (getKeysHeld() & KEY_L) consolePrintf("%d, %d penX=%d, penY=%d tz=%d\n", IPC->touchXpx, IPC->touchYpx, penX, penY, IPC->touchZ1);
+
+ if ((penDownFrames > 1)) { // Is this right? Dunno, but it works for me.
+
+ if ((penHeld)) {
+ penHeld = true;
+ penDown = false;
+
+ if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) {
+ penX = IPC->touchXpx + touchXOffset;
+ penY = IPC->touchYpx + touchYOffset;
+ }
+
+ } else {
+ penDown = true;
+ penHeld = true;
+ penDownSaved = true;
+
+ //if ( (ABS(penX - IPC->touchXpx) < 10) && (ABS(penY - IPC->touchYpx) < 10) ) {
+ if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) {
+ penX = IPC->touchXpx;
+ penY = IPC->touchYpx;
+ }
+ //}
+ }
+
+ } else {
+ if (penHeld) {
+ penReleased = true;
+ penReleasedSaved = true;
+ } else {
+ penReleased = false;
+ }
+
+ penDown = false;
+ penHeld = false;
+ }
+
+
+ if ((IPC->touchZ1 > 0) || ((penDownFrames == 2)) ) {
+ penDownLastFrame = true;
+ penDownFrames++;
+ } else {
+ penDownLastFrame = false;
+ penDownFrames = 0;
+ }
+
+}
+
+int leftHandedSwap(int keys) {
+ // Start and select are unchanged
+ if (leftHandedMode) {
+ int result = keys & (~(KEY_R | KEY_L | KEY_Y | KEY_A | KEY_B | KEY_X | KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN));
+
+ if (keys & KEY_L) result |= KEY_R;
+ if (keys & KEY_R) result |= KEY_L;
+
+ if (keys & KEY_LEFT) result |= KEY_Y;
+ if (keys & KEY_RIGHT) result |= KEY_A;
+ if (keys & KEY_DOWN) result |= KEY_B;
+ if (keys & KEY_UP) result |= KEY_X;
+
+ if (keys & KEY_Y) result |= KEY_LEFT;
+ if (keys & KEY_A) result |= KEY_RIGHT;
+ if (keys & KEY_B) result |= KEY_DOWN;
+ if (keys & KEY_X) result |= KEY_UP;
+
+ return result;
+ } else {
+ return keys;
+ }
+}
+
+void keysUpdate() {
+ scanKeys();
+ keysDownSaved |= leftHandedSwap(keysDown());
+ keysReleasedSaved |= leftHandedSwap(keysUp());
+}
+
+int getKeysDown() {
+ return keysDownSaved;
+}
+
+int getKeysHeld() {
+ return leftHandedSwap(keysHeld());
+}
+
+int getKeysReleased() {
+ return keysReleasedSaved;
+}
+
+void consumeKeys() {
+ keysDownSaved = 0;
+ keysReleasedSaved = 0;
+}
+
+bool getPenDown() {
+ return penDownSaved;
+}
+
+bool getPenHeld() {
+ return penHeld;
+}
+
+bool getPenReleased() {
+ return penReleasedSaved;
+}
+
+void consumePenEvents() {
+ penDownSaved = false;
+ penReleasedSaved = false;
+}
+
+int getPenX() {
+ int x = ((penX * touchScX) >> 8) + touchX;
+ x = x < 0? 0: (x > gameWidth - 1? gameWidth - 1: x);
+ return x;
+}
+
+int getPenY() {
+ int y = ((penY * touchScY) >> 8) + touchY;
+ y = y < 0? 0: (y > gameHeight - 1? gameHeight - 1: y);
+ return y;
+}
+
+GLvector getPenPos() {
+ GLvector v;
+
+ v.x = (penX * inttof32(1)) / SCREEN_WIDTH;
+ v.y = (penY * inttof32(1)) / SCREEN_HEIGHT;
+
+ return v;
+}
+
+void formatSramOption() {
+ consolePrintf("The following files are present in save RAM:\n");
+ DSSaveFileManager::instance()->listFiles();
+
+ consolePrintf("\nAre you sure you want to\n");
+ consolePrintf("DELETE all files?\n");
+ consolePrintf("A = Yes, X = No\n");
+
+ while (true) {
+ if (keysHeld() & KEY_A) {
+ DSSaveFileManager::instance()->formatSram();
+ consolePrintf("SRAM cleared!\n");
+ return;
+ }
+
+ if (keysHeld() & KEY_X) {
+ consolePrintf("Whew, that was close!\n");
+ return;
+ }
+ }
+}
+
+
+void setIndyFightState(bool st) {
+ indyFightState = st;
+ indyFightRight = true;
+}
+
+bool getIndyFightState() {
+ return indyFightState;
+}
+
+/////////////////
+// GBAMP
+/////////////////
+
+bool GBAMPAvail = false;
+
+void initGBAMP() {
+ FAT_InitFiles();
+ if (disc_IsInserted()) {
+ GBAMPAvail = true;
+ consolePrintf("Found flash card reader!\n");
+ } else {
+ GBAMPAvail = false;
+ consolePrintf("Flash card reader not found!\n");
+ }
+}
+
+bool isGBAMPAvailable() {
+ return GBAMPAvail;
+}
+
+
+
+/////////////////
+// Main
+/////////////////
+
+static const Common::String test("poo", 1, 16);
+
+
+
+int main(void)
+{
+ soundCallback = NULL;
+
+
+ initHardware();
+
+ // Let arm9 read cartridge
+ *((u16 *) (0x04000204)) &= ~0x0080;
+
+ lastCallbackFrame = 0;
+ tweak = 0;
+
+ indyFightState = false;
+ indyFightRight = true;
+
+ // CPU speed = 67108864
+ // 8 frames = 2946 368.5 bytes per fr
+
+// playSound(stretch, 47619, false);
+// playSound(twang, 11010, true); // 18640
+
+// bufferSize = 10;
+ bufferRate = 22050;
+ bufferFrame = 0;
+// bufferSamples = (bufferRate * bufferSize) / 60;
+ bufferSamples = 4096;
+
+ bufferFirstHalf = false;
+ bufferSecondHalf = true;
+
+ lastEventFrame = 0;
+ mouseMode = MOUSE_LEFT;
+
+
+
+
+ int bytes = (2 * (bufferSamples)) + 100;
+
+ soundBuffer = (s16 *) malloc(bytes * 2);
+
+
+ soundHiPart = true;
+/*
+ TIMER1_CR = 0;
+ TIMER1_DATA = TIMER_FREQ(bufferRate);
+ TIMER1_CR = TIMER_ENABLE | TIMER_DIV_1;
+
+ TIMER2_CR = 0;
+ TIMER2_DATA = 0xFFFF - (bufferSamples / 2);
+ TIMER2_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
+ */
+ // 2945 - 2947
+
+
+
+// for (int r = 2946; r < 3000; r++) {
+// soundBuffer[r] = 30000;
+// }
+
+
+
+ consolePrintf("------------------------\n");
+ consolePrintf("ScummVM DS\n");
+ consolePrintf("Ported by Neil Millstone\n");
+#ifdef DS_SCUMM_BUILD
+ consolePrintf("Version 0.61 build A\n");
+#else
+ consolePrintf("Version 0.61 build B\n");
+#endif
+ consolePrintf("------------------------\n");
+ consolePrintf("L/R + D-pad/pen: Scroll view\n");
+ consolePrintf("D-pad left: Left mouse button\n");
+ consolePrintf("D-pad right: Right mouse button\n");
+ consolePrintf("D-pad up: Hover mouse\n");
+ consolePrintf("D-pad down: Skip dialog line\n");
+ consolePrintf("B button: Skip cutscenes\n");
+ consolePrintf("Select: DS Options menu\n");
+ consolePrintf("Start: Game menu\n");
+ consolePrintf("Y (in game): Toggle console\n");
+ consolePrintf("X: Toggle keyboard\n");
+ consolePrintf("A: Swap screens\n");
+ consolePrintf("L + R on bootup: Clear SRAM\n\n");
+ consolePrintf("For a complete poo list see the\n");
+ consolePrintf("help screen.\npoo\n");
+
+
+ for (int r = 0; r < bytes; r++) {
+ soundBuffer[r] = 0;
+ }
+
+ consolePrintf("length=%d str='%s'\n", test.size(), test.c_str());
+
+ swiWaitForVBlank();
+ swiWaitForVBlank();
+ playSound(soundBuffer, (bufferSamples * 2), true);
+ swiWaitForVBlank();
+ swiWaitForVBlank();
+ swiWaitForVBlank();
+
+
+
+ // Create a file system node to force search for a zip file in GBA rom space
+ DSFileSystemNode* node = new DSFileSystemNode();
+ if (!node->getZip() || (!node->getZip()->isReady())) {
+ // If not found, init CF/SD driver
+ initGBAMP();
+ }
+ delete node;
+
+
+
+ updateStatus();
+
+
+// OSystem_DS::instance();
+ g_system = new OSystem_DS();
+ assert(g_system);
+
+ if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) {
+ formatSramOption();
+ }
+
+// printf("'%s'", Common::ConfigManager::kTransientDomain.c_str());
+ //printf("'%s'", Common::ConfigManager::kApplicationDomain.c_str());
+
+
+ char* argv[2] = {"/scummvmds", "--config=scummvmb.ini"};
+#ifdef DS_NON_SCUMM_BUILD
+
+ while (1) {
+ scummvm_main(2, (char **) &argv);
+ }
+#else
+ while (1) {
+ scummvm_main(1, (char **) &argv);
+ }
+#endif
+
+
+ return 0;
+}
+
+}
+
+int main() {
+ DS::main();
+}
diff --git a/backends/platform/ds/arm9/source/dsmain.h b/backends/platform/ds/arm9/source/dsmain.h
new file mode 100644
index 0000000000..8c81b0f5e6
--- /dev/null
+++ b/backends/platform/ds/arm9/source/dsmain.h
@@ -0,0 +1,236 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DSMAIN_H
+#define _DSMAIN_H
+
+#include <nds.h>
+#include "stdafx.h"
+#include "system.h"
+#include "scummconsole.h"
+#include "nds/scummvm_ipc.h"
+
+namespace DS {
+
+// Pen reading functions
+void penInit();
+void penUpdate();
+bool getPenDown();
+bool getPenHeld();
+bool getPenReleased();
+int getPenX();
+int getPenY();
+GLvector getPenPos();
+void consumePenEvents();
+
+// Pad reading
+int getKeysHeld();
+void keysUpdate();
+int getKeysDown();
+int getKeysReleased();
+void consumeKeys();
+int leftHandedSwap(int keys);
+
+// Video
+void displayMode8Bit(); // Switch to 8-bit mode5
+void displayMode16Bit(); // Switch to 16-bit mode5
+
+// Flip double buffer
+void displayMode16BitFlipBuffer();
+
+// Get address of current back buffer
+u16* get16BitBackBuffer();
+u16* get8BitBackBuffer();
+
+void setTalkPos(int x, int y);
+void setTopScreenTarget(int x, int y);
+
+// Timers
+void setTimerCallback(OSystem::TimerProc proc, int interval); // Setup a callback function at a regular interval
+int getMillis(); // Return the current runtime in milliseconds
+void doTimerCallback(); // Call callback function if required
+
+// Sound
+void setSoundProc(OSystem::SoundProc proc, void* param); // Setup a callback function for sound
+void doSoundCallback(); // Call function if sound buffers need more data
+void playSound(const void* data, u32 length, bool loop, bool adpcm = false, int rate = 22050); // Start a sound
+void stopSound(int channel);
+
+// Event queue
+void addEventsToQueue();
+void VBlankHandler();
+
+// Sam and Max Stuff
+void setGameID(int id);
+void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor);
+
+// Shake
+void setShakePos(int shakePos);
+
+// Reports
+void memoryReport();
+
+// GBAMP
+bool isGBAMPAvailable();
+
+// Sleep (I'd like some of that right now)
+void checkSleepMode();
+
+// Virtual keyboard
+void setKeyboardIcon(bool enable);
+bool getKeyboardIcon();
+void setKeyboardEnable(bool en);
+bool getKeyboardEnable();
+
+// Options
+void setLeftHanded(bool enable);
+void setTouchXOffset(int x);
+void setTouchYOffset(int y);
+void setUnscaledMode(bool enable);
+void setIndyFightState(bool st);
+bool getIndyFightState();
+
+// Display
+bool getIsDisplayMode8Bit();
+void setGameSize(int width, int height);
+int getGameWidth();
+int getGameHeight();
+
+}
+
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DSMAIN_H
+#define _DSMAIN_H
+
+#include <nds.h>
+#include "stdafx.h"
+#include "system.h"
+#include "scummconsole.h"
+#include "nds/scummvm_ipc.h"
+
+namespace DS {
+
+// Pen reading functions
+void penInit();
+void penUpdate();
+bool getPenDown();
+bool getPenHeld();
+bool getPenReleased();
+int getPenX();
+int getPenY();
+GLvector getPenPos();
+void consumePenEvents();
+
+// Pad reading
+int getKeysHeld();
+void keysUpdate();
+int getKeysDown();
+int getKeysReleased();
+void consumeKeys();
+int leftHandedSwap(int keys);
+
+// Video
+void displayMode8Bit(); // Switch to 8-bit mode5
+void displayMode16Bit(); // Switch to 16-bit mode5
+
+// Flip double buffer
+void displayMode16BitFlipBuffer();
+
+// Get address of current back buffer
+u16* get16BitBackBuffer();
+u16* get8BitBackBuffer();
+
+void setTalkPos(int x, int y);
+void setTopScreenTarget(int x, int y);
+
+// Timers
+void setTimerCallback(OSystem::TimerProc proc, int interval); // Setup a callback function at a regular interval
+int getMillis(); // Return the current runtime in milliseconds
+void doTimerCallback(); // Call callback function if required
+
+// Sound
+void setSoundProc(OSystem::SoundProc proc, void* param); // Setup a callback function for sound
+void doSoundCallback(); // Call function if sound buffers need more data
+void playSound(const void* data, u32 length, bool loop, bool adpcm = false, int rate = 22050); // Start a sound
+void stopSound(int channel);
+
+// Event queue
+void addEventsToQueue();
+void VBlankHandler();
+
+// Sam and Max Stuff
+void setGameID(int id);
+void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor);
+
+// Shake
+void setShakePos(int shakePos);
+
+// Reports
+void memoryReport();
+
+// GBAMP
+bool isGBAMPAvailable();
+
+// Sleep (I'd like some of that right now)
+void checkSleepMode();
+
+// Virtual keyboard
+void setKeyboardIcon(bool enable);
+bool getKeyboardIcon();
+void setKeyboardEnable(bool en);
+bool getKeyboardEnable();
+
+// Options
+void setLeftHanded(bool enable);
+void setTouchXOffset(int x);
+void setTouchYOffset(int y);
+void setUnscaledMode(bool enable);
+void setIndyFightState(bool st);
+bool getIndyFightState();
+
+// Display
+bool getIsDisplayMode8Bit();
+void setGameSize(int width, int height);
+int getGameWidth();
+int getGameHeight();
+
+}
+
+
+#endif
diff --git a/backends/platform/ds/arm9/source/dsoptions.cpp b/backends/platform/ds/arm9/source/dsoptions.cpp
new file mode 100644
index 0000000000..04db01389b
--- /dev/null
+++ b/backends/platform/ds/arm9/source/dsoptions.cpp
@@ -0,0 +1,412 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "dsoptions.h"
+#include "dsmain.h"
+#include "gui/dialog.h"
+#include "gui/newgui.h"
+#include "gui/listwidget.h"
+#include "osystem_ds.h"
+#include "engines/scumm/scumm.h"
+#include "touchkeyboard.h"
+
+#ifdef DS_SCUMM_BUILD
+namespace Scumm {
+ extern Common::StringList generateSavegameList(Scumm::ScummEngine *scumm, bool saveMode);
+ extern Scumm::ScummEngine *g_scumm;
+}
+#endif
+
+namespace DS {
+
+DSOptionsDialog::DSOptionsDialog() : GUI::Dialog(20, 20, 320 - 40, 200 - 40) {
+ addButton(this, 10, 140, "Close", GUI::kCloseCmd, 'C');
+
+#ifdef DS_SCUMM_BUILD
+ if (!DS::isGBAMPAvailable()) {
+// addButton(this, 100, 140, "Delete Save", 'dels', 'D');
+ }
+#endif
+
+ new GUI::StaticTextWidget(this, 0, 10, 280, 20, "ScummVM DS Options", GUI::kTextAlignCenter);
+
+ _leftHandedCheckbox = new GUI::CheckboxWidget(this, 20, 30, 280, 20, "Left handed mode", 0, 'L');
+ _indyFightCheckbox = new GUI::CheckboxWidget(this, 20, 50, 280, 20, "Indy fighting controls", 0, 'I');
+ _unscaledCheckbox = new GUI::CheckboxWidget(this, 20, 70, 280, 20, "Unscaled lower screen", 0, 'S');
+
+ new GUI::StaticTextWidget(this, 20, 90, 110, 20, "Touch X Offset", GUI::kTextAlignLeft);
+ _touchX = new GUI::SliderWidget(this, 130, 90, 130, 12, 1);
+ _touchX->setMinValue(-8);
+ _touchX->setMaxValue(+8);
+ _touchX->setValue(0);
+ _touchX->setFlags(GUI::WIDGET_CLEARBG);
+
+ new GUI::StaticTextWidget(this, 20, 110, 110, 20, "Touch Y Offset", GUI::kTextAlignLeft);
+ _touchY = new GUI::SliderWidget(this, 130, 110, 130, 12, 2);
+ _touchY->setMinValue(-8);
+ _touchY->setMaxValue(+8);
+ _touchY->setValue(0);
+ _touchY->setFlags(GUI::WIDGET_CLEARBG);
+
+ new GUI::StaticTextWidget(this, 130 + 65 - 10, 130, 20, 20, "0", GUI::kTextAlignCenter);
+ new GUI::StaticTextWidget(this, 130 + 130 - 10, 130, 20, 20, "8", GUI::kTextAlignCenter);
+ new GUI::StaticTextWidget(this, 130 - 10, 130, 20, 20, "-8", GUI::kTextAlignCenter);
+
+#ifdef DS_SCUMM_BUILD
+ _delDialog = new Scumm::SaveLoadChooser("Delete game:", "Delete", false, Scumm::g_scumm);
+#endif
+
+ if (ConfMan.hasKey("lefthanded", "ds")) {
+ _leftHandedCheckbox->setState(ConfMan.getBool("lefthanded", "ds"));
+ } else {
+ _leftHandedCheckbox->setState(false);
+ }
+
+ if (ConfMan.hasKey("unscaled", "ds")) {
+ _unscaledCheckbox->setState(ConfMan.getBool("unscaled", "ds"));
+ } else {
+ _unscaledCheckbox->setState(false);
+ }
+
+ _indyFightCheckbox->setState(DS::getIndyFightState());
+
+ if (ConfMan.hasKey("xoffset", "ds")) {
+ _touchX->setValue(ConfMan.getInt("xoffset", "ds"));
+ } else {
+ _touchX->setValue(0);
+ }
+
+ if (ConfMan.hasKey("yoffset", "ds")) {
+ _touchY->setValue(ConfMan.getInt("yoffset", "ds"));
+ } else {
+ _touchY->setValue(0);
+ }
+
+}
+
+DSOptionsDialog::~DSOptionsDialog() {
+ ConfMan.setBool("lefthanded", _leftHandedCheckbox->getState(), "ds");
+ ConfMan.setBool("unscaled", _unscaledCheckbox->getState(), "ds");
+ ConfMan.setInt("xoffset", _touchX->getValue(), "ds");
+ ConfMan.setInt("yoffset", _touchY->getValue(), "ds");
+ DS::setOptions();
+ DS::setIndyFightState(_indyFightCheckbox->getState());
+ ConfMan.flushToDisk();
+}
+
+
+void DSOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ if (cmd == GUI::kCloseCmd) {
+ close();
+ }
+
+#ifdef DS_SCUMM_BUILD
+/* if (cmd == 'dels') {
+ _delDialog->setList(Scumm::generateSavegameList(Scumm::g_scumm, false));
+ _delDialog->handleCommand(NULL, GUI::kListSelectionChangedCmd, 0);
+
+ OSystem::Event event;
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.ascii = SDLK_DOWN;
+ event.kbd.keycode = SDLK_DOWN;
+ OSystem_DS::instance()->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ OSystem_DS::instance()->addEvent(event);
+
+ int idx = _delDialog->runModal();
+
+ if (idx >= 0) {
+ char name[256];
+ Scumm::g_scumm->makeSavegameName(name, idx, false);
+ if (!DS::isGBAMPAvailable()) {
+ ((DSSaveFileManager *) (OSystem_DS::instance()->getSavefileManager()))->deleteFile(name);
+ }
+ }
+
+ }*/
+#endif
+
+
+}
+
+void showOptionsDialog() {
+ OSystem_DS* system = OSystem_DS::instance();
+
+ OSystem::Event event;
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 'P'; // F5
+ event.kbd.ascii = 'P';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ DS::displayMode16Bit();
+
+
+ DSOptionsDialog* d = new DSOptionsDialog();
+ d->runModal();
+ delete d;
+
+
+ DS::displayMode8Bit();
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 'P'; // F5
+ event.kbd.ascii = 'P';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+}
+
+void setOptions() {
+ ConfMan.addGameDomain("ds");
+
+ if (ConfMan.hasKey("lefthanded", "ds")) {
+ DS::setLeftHanded(ConfMan.getBool("lefthanded", "ds"));
+ } else {
+ DS::setLeftHanded(false);
+ }
+
+ if (ConfMan.hasKey("unscaled", "ds")) {
+ DS::setUnscaledMode(ConfMan.getBool("unscaled", "ds"));
+ } else {
+ DS::setUnscaledMode(false);
+ }
+
+ if (ConfMan.hasKey("xoffset", "ds")) {
+ DS::setTouchXOffset(ConfMan.getInt("xoffset", "ds"));
+ } else {
+ DS::setTouchXOffset(0);
+ }
+
+ if (ConfMan.hasKey("yoffset", "ds")) {
+ DS::setTouchYOffset(ConfMan.getInt("yoffset", "ds"));
+ } else {
+ DS::setTouchXOffset(0);
+ }
+
+}
+
+}
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "dsoptions.h"
+#include "dsmain.h"
+#include "gui/dialog.h"
+#include "gui/newgui.h"
+#include "gui/listwidget.h"
+#include "osystem_ds.h"
+#include "engines/scumm/scumm.h"
+#include "touchkeyboard.h"
+
+#ifdef DS_SCUMM_BUILD
+namespace Scumm {
+ extern Common::StringList generateSavegameList(Scumm::ScummEngine *scumm, bool saveMode);
+ extern Scumm::ScummEngine *g_scumm;
+}
+#endif
+
+namespace DS {
+
+DSOptionsDialog::DSOptionsDialog() : GUI::Dialog(20, 20, 320 - 40, 200 - 40) {
+ addButton(this, 10, 140, "Close", GUI::kCloseCmd, 'C');
+
+#ifdef DS_SCUMM_BUILD
+ if (!DS::isGBAMPAvailable()) {
+// addButton(this, 100, 140, "Delete Save", 'dels', 'D');
+ }
+#endif
+
+ new GUI::StaticTextWidget(this, 0, 10, 280, 20, "ScummVM DS Options", GUI::kTextAlignCenter);
+
+ _leftHandedCheckbox = new GUI::CheckboxWidget(this, 20, 30, 280, 20, "Left handed mode", 0, 'L');
+ _indyFightCheckbox = new GUI::CheckboxWidget(this, 20, 50, 280, 20, "Indy fighting controls", 0, 'I');
+ _unscaledCheckbox = new GUI::CheckboxWidget(this, 20, 70, 280, 20, "Unscaled lower screen", 0, 'S');
+
+ new GUI::StaticTextWidget(this, 20, 90, 110, 20, "Touch X Offset", GUI::kTextAlignLeft);
+ _touchX = new GUI::SliderWidget(this, 130, 90, 130, 12, 1);
+ _touchX->setMinValue(-8);
+ _touchX->setMaxValue(+8);
+ _touchX->setValue(0);
+ _touchX->setFlags(GUI::WIDGET_CLEARBG);
+
+ new GUI::StaticTextWidget(this, 20, 110, 110, 20, "Touch Y Offset", GUI::kTextAlignLeft);
+ _touchY = new GUI::SliderWidget(this, 130, 110, 130, 12, 2);
+ _touchY->setMinValue(-8);
+ _touchY->setMaxValue(+8);
+ _touchY->setValue(0);
+ _touchY->setFlags(GUI::WIDGET_CLEARBG);
+
+ new GUI::StaticTextWidget(this, 130 + 65 - 10, 130, 20, 20, "0", GUI::kTextAlignCenter);
+ new GUI::StaticTextWidget(this, 130 + 130 - 10, 130, 20, 20, "8", GUI::kTextAlignCenter);
+ new GUI::StaticTextWidget(this, 130 - 10, 130, 20, 20, "-8", GUI::kTextAlignCenter);
+
+#ifdef DS_SCUMM_BUILD
+ _delDialog = new Scumm::SaveLoadChooser("Delete game:", "Delete", false, Scumm::g_scumm);
+#endif
+
+ if (ConfMan.hasKey("lefthanded", "ds")) {
+ _leftHandedCheckbox->setState(ConfMan.getBool("lefthanded", "ds"));
+ } else {
+ _leftHandedCheckbox->setState(false);
+ }
+
+ if (ConfMan.hasKey("unscaled", "ds")) {
+ _unscaledCheckbox->setState(ConfMan.getBool("unscaled", "ds"));
+ } else {
+ _unscaledCheckbox->setState(false);
+ }
+
+ _indyFightCheckbox->setState(DS::getIndyFightState());
+
+ if (ConfMan.hasKey("xoffset", "ds")) {
+ _touchX->setValue(ConfMan.getInt("xoffset", "ds"));
+ } else {
+ _touchX->setValue(0);
+ }
+
+ if (ConfMan.hasKey("yoffset", "ds")) {
+ _touchY->setValue(ConfMan.getInt("yoffset", "ds"));
+ } else {
+ _touchY->setValue(0);
+ }
+
+}
+
+DSOptionsDialog::~DSOptionsDialog() {
+ ConfMan.setBool("lefthanded", _leftHandedCheckbox->getState(), "ds");
+ ConfMan.setBool("unscaled", _unscaledCheckbox->getState(), "ds");
+ ConfMan.setInt("xoffset", _touchX->getValue(), "ds");
+ ConfMan.setInt("yoffset", _touchY->getValue(), "ds");
+ DS::setOptions();
+ DS::setIndyFightState(_indyFightCheckbox->getState());
+ ConfMan.flushToDisk();
+}
+
+
+void DSOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ if (cmd == GUI::kCloseCmd) {
+ close();
+ }
+
+#ifdef DS_SCUMM_BUILD
+/* if (cmd == 'dels') {
+ _delDialog->setList(Scumm::generateSavegameList(Scumm::g_scumm, false));
+ _delDialog->handleCommand(NULL, GUI::kListSelectionChangedCmd, 0);
+
+ OSystem::Event event;
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.ascii = SDLK_DOWN;
+ event.kbd.keycode = SDLK_DOWN;
+ OSystem_DS::instance()->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ OSystem_DS::instance()->addEvent(event);
+
+ int idx = _delDialog->runModal();
+
+ if (idx >= 0) {
+ char name[256];
+ Scumm::g_scumm->makeSavegameName(name, idx, false);
+ if (!DS::isGBAMPAvailable()) {
+ ((DSSaveFileManager *) (OSystem_DS::instance()->getSavefileManager()))->deleteFile(name);
+ }
+ }
+
+ }*/
+#endif
+
+
+}
+
+void showOptionsDialog() {
+ OSystem_DS* system = OSystem_DS::instance();
+
+ OSystem::Event event;
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 'P'; // F5
+ event.kbd.ascii = 'P';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ DS::displayMode16Bit();
+
+
+ DSOptionsDialog* d = new DSOptionsDialog();
+ d->runModal();
+ delete d;
+
+
+ DS::displayMode8Bit();
+
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.keycode = 'P'; // F5
+ event.kbd.ascii = 'P';
+ event.kbd.flags = 0;
+ system->addEvent(event);
+}
+
+void setOptions() {
+ ConfMan.addGameDomain("ds");
+
+ if (ConfMan.hasKey("lefthanded", "ds")) {
+ DS::setLeftHanded(ConfMan.getBool("lefthanded", "ds"));
+ } else {
+ DS::setLeftHanded(false);
+ }
+
+ if (ConfMan.hasKey("unscaled", "ds")) {
+ DS::setUnscaledMode(ConfMan.getBool("unscaled", "ds"));
+ } else {
+ DS::setUnscaledMode(false);
+ }
+
+ if (ConfMan.hasKey("xoffset", "ds")) {
+ DS::setTouchXOffset(ConfMan.getInt("xoffset", "ds"));
+ } else {
+ DS::setTouchXOffset(0);
+ }
+
+ if (ConfMan.hasKey("yoffset", "ds")) {
+ DS::setTouchYOffset(ConfMan.getInt("yoffset", "ds"));
+ } else {
+ DS::setTouchXOffset(0);
+ }
+
+}
+
+}
+
diff --git a/backends/platform/ds/arm9/source/dsoptions.h b/backends/platform/ds/arm9/source/dsoptions.h
new file mode 100644
index 0000000000..ce3d77c7da
--- /dev/null
+++ b/backends/platform/ds/arm9/source/dsoptions.h
@@ -0,0 +1,122 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DSOPTIONS_H_
+#define _DSOPTIONS_H_
+
+#include "stdafx.h"
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+#include "gui/object.h"
+#include "gui/widget.h"
+#include "gui/dialog.h"
+#include "scumm/dialogs.h"
+
+namespace DS {
+
+class DSOptionsDialog : public GUI::Dialog {
+
+public:
+ DSOptionsDialog();
+ ~DSOptionsDialog();
+
+protected:
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+
+
+ GUI::SliderWidget* _touchX;
+ GUI::SliderWidget* _touchY;
+ GUI::CheckboxWidget* _leftHandedCheckbox;
+ GUI::CheckboxWidget* _unscaledCheckbox;
+ GUI::CheckboxWidget* _indyFightCheckbox;
+#ifdef DS_SCUMM_BUILD
+ Scumm::SaveLoadChooser* _delDialog;
+#endif
+
+};
+
+extern void showOptionsDialog();
+extern void setOptions();
+
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DSOPTIONS_H_
+#define _DSOPTIONS_H_
+
+#include "stdafx.h"
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+#include "gui/object.h"
+#include "gui/widget.h"
+#include "gui/dialog.h"
+#include "scumm/dialogs.h"
+
+namespace DS {
+
+class DSOptionsDialog : public GUI::Dialog {
+
+public:
+ DSOptionsDialog();
+ ~DSOptionsDialog();
+
+protected:
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+
+
+ GUI::SliderWidget* _touchX;
+ GUI::SliderWidget* _touchY;
+ GUI::CheckboxWidget* _leftHandedCheckbox;
+ GUI::CheckboxWidget* _unscaledCheckbox;
+ GUI::CheckboxWidget* _indyFightCheckbox;
+#ifdef DS_SCUMM_BUILD
+ Scumm::SaveLoadChooser* _delDialog;
+#endif
+
+};
+
+extern void showOptionsDialog();
+extern void setOptions();
+
+}
+
+#endif
diff --git a/backends/platform/ds/arm9/source/fat/disc_io.c b/backends/platform/ds/arm9/source/fat/disc_io.c
new file mode 100644
index 0000000000..f2fbee5d65
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/disc_io.c
@@ -0,0 +1,844 @@
+/*
+
+ disc_io.c
+
+ uniformed io-interface to work with Chishm's FAT library
+
+ Written by MightyMax
+
+ Modified by Chishm:
+ 2005-11-06
+ * Added WAIT_CR modifications for NDS
+
+ Modified by www.neoflash.com:
+ 2006-02-03
+ * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
+ for the given interface and stop code being linked to the binary
+
+ * Added support for MK2 MMC interface
+
+ * Added disc_Cache* functions
+
+ Modified by Chishm:
+ 2006-02-05
+ * Added Supercard SD support
+
+ Modified by CyteX:
+ 2006-02-26
+ * Added EFA2 support
+*/
+
+#include "disc_io.h"
+
+#ifdef NDS
+ #include <nds.h>
+#endif
+
+
+// Include known io-interfaces:
+#ifdef SUPPORT_MPCF
+ #include "io_mpcf.h"
+#endif
+
+#ifdef SUPPORT_M3CF
+ #include "io_m3cf.h"
+#endif
+
+#ifdef SUPPORT_M3SD
+ #include "io_m3sd.h"
+#endif
+
+#ifdef SUPPORT_SCCF
+ #include "io_sccf.h"
+#endif
+
+#ifdef SUPPORT_SCSD
+ #include "io_scsd.h"
+#endif
+
+#ifdef SUPPORT_FCSR
+ #include "io_fcsr.h"
+#endif
+
+#ifdef SUPPORT_NMMC
+ #include "io_nmmc.h"
+#endif
+
+#ifdef SUPPORT_EFA2
+ #include "io_efa2.h"
+#endif
+
+// Keep a pointer to the active interface
+LPIO_INTERFACE active_interface = 0;
+
+
+/*
+
+ Disc Cache functions
+ 2006-02-03:
+ Added by www.neoflash.com
+
+*/
+
+#ifdef DISC_CACHE
+
+#include <string.h>
+
+#define CACHE_FREE 0xFFFFFFFF
+
+static u8 cacheBuffer[ DISC_CACHE_COUNT * 512 ];
+
+static struct {
+ u32 sector;
+ u32 dirty;
+ u32 count;
+} cache[ DISC_CACHE_COUNT ];
+
+static u32 disc_CacheFind(u32 sector) {
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector == sector )
+ return i;
+ }
+
+ return CACHE_FREE;
+}
+
+static u32 disc_CacheFindFree(void) {
+
+ u32 i = 0, j;
+ u32 count = -1;
+
+ for( j = 0; j < DISC_CACHE_COUNT; j++ ) {
+
+ if( cache[ j ].sector == CACHE_FREE ) {
+ i = j;
+ break;
+ }
+
+ if( cache[ j ].count < count ) {
+ count = cache[ j ].count;
+ i = j;
+ }
+ }
+
+ if( cache[ i ].sector != CACHE_FREE && cache[i].dirty != 0 ) {
+
+ active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] );
+ /* todo: handle write error here */
+
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+ return i;
+}
+
+void disc_CacheInit(void) {
+
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+}
+
+bool disc_CacheFlush(void) {
+
+ u32 i;
+
+ if( !active_interface ) return false;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector != CACHE_FREE && cache[ i ].dirty != 0 ) {
+ if( active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+
+ cache[ i ].dirty = 0;
+ }
+ }
+ return true;
+}
+
+bool disc_CacheReadSector( void *buffer, u32 sector) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache[ i ].sector = sector;
+ if( active_interface->fn_ReadSectors( sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)&cacheBuffer[ i * 512 ]
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( buffer, &cacheBuffer[ i * 512 ], 512 );
+#endif
+ cache[ i ].count++;
+ return true;
+}
+
+bool disc_CacheWriteSector( void *buffer, u32 sector ) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache [ i ].sector = sector;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)buffer;
+ DMA3_DEST = (u32)&cacheBuffer[ i * 512 ];
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( &cacheBuffer[ i * 512 ], buffer, 512 );
+#endif
+ cache[ i ].dirty=1;
+ cache[ i ].count++;
+ return true;
+}
+
+#endif
+
+/*
+
+ Hardware level disc funtions
+
+*/
+
+bool disc_setGbaSlotInterface (void)
+{
+ // If running on an NDS, make sure the correct CPU can access
+ // the GBA cart. First implemented by SaTa.
+#ifdef NDS
+ #ifdef ARM9
+// WAIT_CR &= ~(0x8080);
+ #endif
+ #ifdef ARM7
+// WAIT_CR |= (0x8080);
+ #endif
+#endif
+
+#ifdef SUPPORT_M3CF
+ // check if we have a M3 perfect CF plugged in
+ active_interface = M3CF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_M3SD
+ // check if we have a M3 perfect SD plugged in
+ active_interface = M3SD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 SD as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_MPCF
+ // check if we have a GBA Movie Player plugged in
+ active_interface = MPCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set GBAMP as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCCF
+ // check if we have a SuperCard CF plugged in
+ active_interface = SCCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCSD
+ // check if we have a SuperCard SD plugged in
+ active_interface = SCSD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC SD as default IO
+ return true ;
+ } ;
+#endif
+
+
+#ifdef SUPPORT_EFA2
+ // check if we have a EFA2 plugged in
+ active_interface = EFA2_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_FCSR
+ // check if we have a GBA Flash Cart plugged in
+ active_interface = FCSR_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set FC as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+
+#ifdef NDS
+// Check the DS card slot for a valid memory card interface
+// If an interface is found, it is set as the default interace
+// and it returns true. Otherwise the default interface is left
+// untouched and it returns false.
+bool disc_setDsSlotInterface (void)
+{
+#ifdef ARM9
+ WAIT_CR &= ~(1<<11);
+#endif
+#ifdef ARM7
+ WAIT_CR |= (1<<11);
+#endif
+
+#ifdef SUPPORT_NMMC
+ // check if we have a Neoflash MK2 / MK3 plugged in
+ active_interface = NMMC_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set Neoflash MK2 / MK3 as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+#endif
+
+
+bool disc_Init(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheInit();
+#endif
+
+
+ if (active_interface != 0) {
+ return true;
+ }
+
+#ifdef NDS
+ if (disc_setDsSlotInterface()) {
+ return true;
+ }
+#endif
+
+ if (disc_setGbaSlotInterface()) {
+ return true;
+ }
+
+ // could not find a working IO Interface
+ active_interface = 0 ;
+ return false ;
+}
+
+bool disc_IsInserted(void)
+{
+ if (active_interface) return active_interface->fn_IsInserted() ;
+ return false ;
+}
+
+bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheReadSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_ReadSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheWriteSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_WriteSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_ClearStatus(void)
+{
+ if (active_interface) return active_interface->fn_ClearStatus() ;
+ return false ;
+}
+
+bool disc_Shutdown(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheFlush();
+#endif
+ if (active_interface) active_interface->fn_Shutdown() ;
+ active_interface = 0 ;
+ return true ;
+}
+
+u32 disc_HostType (void)
+{
+ if (active_interface) {
+ return active_interface->ul_ioType;
+ } else {
+ return 0;
+ }
+}
+
+/*
+
+ disc_io.c
+
+ uniformed io-interface to work with Chishm's FAT library
+
+ Written by MightyMax
+
+ Modified by Chishm:
+ 2005-11-06
+ * Added WAIT_CR modifications for NDS
+
+ Modified by www.neoflash.com:
+ 2006-02-03
+ * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
+ for the given interface and stop code being linked to the binary
+
+ * Added support for MK2 MMC interface
+
+ * Added disc_Cache* functions
+
+ Modified by Chishm:
+ 2006-02-05
+ * Added Supercard SD support
+
+ Modified by CyteX:
+ 2006-02-26
+ * Added EFA2 support
+*/
+
+#include "disc_io.h"
+
+#ifdef NDS
+ #include <nds.h>
+#endif
+
+
+// Include known io-interfaces:
+#ifdef SUPPORT_MPCF
+ #include "io_mpcf.h"
+#endif
+
+#ifdef SUPPORT_M3CF
+ #include "io_m3cf.h"
+#endif
+
+#ifdef SUPPORT_M3SD
+ #include "io_m3sd.h"
+#endif
+
+#ifdef SUPPORT_SCCF
+ #include "io_sccf.h"
+#endif
+
+#ifdef SUPPORT_SCSD
+ #include "io_scsd.h"
+#endif
+
+#ifdef SUPPORT_FCSR
+ #include "io_fcsr.h"
+#endif
+
+#ifdef SUPPORT_NMMC
+ #include "io_nmmc.h"
+#endif
+
+#ifdef SUPPORT_EFA2
+ #include "io_efa2.h"
+#endif
+
+// Keep a pointer to the active interface
+LPIO_INTERFACE active_interface = 0;
+
+
+/*
+
+ Disc Cache functions
+ 2006-02-03:
+ Added by www.neoflash.com
+
+*/
+
+#ifdef DISC_CACHE
+
+#include <string.h>
+
+#define CACHE_FREE 0xFFFFFFFF
+
+static u8 cacheBuffer[ DISC_CACHE_COUNT * 512 ];
+
+static struct {
+ u32 sector;
+ u32 dirty;
+ u32 count;
+} cache[ DISC_CACHE_COUNT ];
+
+static u32 disc_CacheFind(u32 sector) {
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector == sector )
+ return i;
+ }
+
+ return CACHE_FREE;
+}
+
+static u32 disc_CacheFindFree(void) {
+
+ u32 i = 0, j;
+ u32 count = -1;
+
+ for( j = 0; j < DISC_CACHE_COUNT; j++ ) {
+
+ if( cache[ j ].sector == CACHE_FREE ) {
+ i = j;
+ break;
+ }
+
+ if( cache[ j ].count < count ) {
+ count = cache[ j ].count;
+ i = j;
+ }
+ }
+
+ if( cache[ i ].sector != CACHE_FREE && cache[i].dirty != 0 ) {
+
+ active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] );
+ /* todo: handle write error here */
+
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+ return i;
+}
+
+void disc_CacheInit(void) {
+
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+}
+
+bool disc_CacheFlush(void) {
+
+ u32 i;
+
+ if( !active_interface ) return false;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector != CACHE_FREE && cache[ i ].dirty != 0 ) {
+ if( active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+
+ cache[ i ].dirty = 0;
+ }
+ }
+ return true;
+}
+
+bool disc_CacheReadSector( void *buffer, u32 sector) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache[ i ].sector = sector;
+ if( active_interface->fn_ReadSectors( sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)&cacheBuffer[ i * 512 ]
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( buffer, &cacheBuffer[ i * 512 ], 512 );
+#endif
+ cache[ i ].count++;
+ return true;
+}
+
+bool disc_CacheWriteSector( void *buffer, u32 sector ) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache [ i ].sector = sector;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)buffer;
+ DMA3_DEST = (u32)&cacheBuffer[ i * 512 ];
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( &cacheBuffer[ i * 512 ], buffer, 512 );
+#endif
+ cache[ i ].dirty=1;
+ cache[ i ].count++;
+ return true;
+}
+
+#endif
+
+/*
+
+ Hardware level disc funtions
+
+*/
+
+bool disc_setGbaSlotInterface (void)
+{
+ // If running on an NDS, make sure the correct CPU can access
+ // the GBA cart. First implemented by SaTa.
+#ifdef NDS
+ #ifdef ARM9
+// WAIT_CR &= ~(0x8080);
+ #endif
+ #ifdef ARM7
+// WAIT_CR |= (0x8080);
+ #endif
+#endif
+
+#ifdef SUPPORT_M3CF
+ // check if we have a M3 perfect CF plugged in
+ active_interface = M3CF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_M3SD
+ // check if we have a M3 perfect SD plugged in
+ active_interface = M3SD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 SD as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_MPCF
+ // check if we have a GBA Movie Player plugged in
+ active_interface = MPCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set GBAMP as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCCF
+ // check if we have a SuperCard CF plugged in
+ active_interface = SCCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCSD
+ // check if we have a SuperCard SD plugged in
+ active_interface = SCSD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC SD as default IO
+ return true ;
+ } ;
+#endif
+
+
+#ifdef SUPPORT_EFA2
+ // check if we have a EFA2 plugged in
+ active_interface = EFA2_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_FCSR
+ // check if we have a GBA Flash Cart plugged in
+ active_interface = FCSR_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set FC as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+
+#ifdef NDS
+// Check the DS card slot for a valid memory card interface
+// If an interface is found, it is set as the default interace
+// and it returns true. Otherwise the default interface is left
+// untouched and it returns false.
+bool disc_setDsSlotInterface (void)
+{
+#ifdef ARM9
+ WAIT_CR &= ~(1<<11);
+#endif
+#ifdef ARM7
+ WAIT_CR |= (1<<11);
+#endif
+
+#ifdef SUPPORT_NMMC
+ // check if we have a Neoflash MK2 / MK3 plugged in
+ active_interface = NMMC_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set Neoflash MK2 / MK3 as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+#endif
+
+
+bool disc_Init(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheInit();
+#endif
+
+
+ if (active_interface != 0) {
+ return true;
+ }
+
+#ifdef NDS
+ if (disc_setDsSlotInterface()) {
+ return true;
+ }
+#endif
+
+ if (disc_setGbaSlotInterface()) {
+ return true;
+ }
+
+ // could not find a working IO Interface
+ active_interface = 0 ;
+ return false ;
+}
+
+bool disc_IsInserted(void)
+{
+ if (active_interface) return active_interface->fn_IsInserted() ;
+ return false ;
+}
+
+bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheReadSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_ReadSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheWriteSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_WriteSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_ClearStatus(void)
+{
+ if (active_interface) return active_interface->fn_ClearStatus() ;
+ return false ;
+}
+
+bool disc_Shutdown(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheFlush();
+#endif
+ if (active_interface) active_interface->fn_Shutdown() ;
+ active_interface = 0 ;
+ return true ;
+}
+
+u32 disc_HostType (void)
+{
+ if (active_interface) {
+ return active_interface->ul_ioType;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/backends/platform/ds/arm9/source/fat/disc_io.h b/backends/platform/ds/arm9/source/fat/disc_io.h
new file mode 100644
index 0000000000..f647f9ac02
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/disc_io.h
@@ -0,0 +1,364 @@
+#ifndef DISC_IO_H
+#define DISC_IO_H
+
+//----------------------------------------------------------------------
+// Customisable features
+
+// Use DMA to read the card, remove this line to use normal reads/writes
+// #define _CF_USE_DMA
+
+// Allow buffers not aligned to 16 bits when reading files.
+// Note that this will slow down access speed, so only use if you have to.
+// It is also incompatible with DMA
+//#define _CF_ALLOW_UNALIGNED
+
+// Device support options, added by www.neoflash.com
+
+#define SUPPORT_NMMC // comment out this line to remove Neoflash MK2 MMC Card support
+#define SUPPORT_MPCF // comment out this line to remove GBA Movie Player support
+#define SUPPORT_M3CF // comment out this line to remove M3 Perfect CF support
+#define SUPPORT_M3SD // comment out this line to remove M3 Perfect SD support
+#define SUPPORT_SCCF // comment out this line to remove Supercard CF support
+#define SUPPORT_SCSD // comment out this line to remove Supercard SD support
+//#define SUPPORT_EFA2 // comment out this line to remove EFA2 linker support
+#define SUPPORT_FCSR // comment out this line to remove GBA Flash Cart support
+
+// Disk caching options, added by www.neoflash.com
+// Each additional sector cache uses 512 bytes of memory
+// Disk caching is disabled on GBA to conserve memory
+
+#define DISC_CACHE // uncomment this line to enable disc caching
+#define DISC_CACHE_COUNT 16 // maximum number of sectors to cache (512 bytes per sector)
+//#define DISK_CACHE_DMA // use DMA for cache copies. If this is enabled, the data buffers must be word aligned
+
+
+//----------------------------------------------------------------------
+
+#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED
+ #error You can't use both DMA and unaligned memory
+#endif
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+// Disable NDS specific hardware and features if running on a GBA
+#ifndef NDS
+ #undef SUPPORT_NMMC
+ #undef DISC_CACHE
+#endif
+
+/*
+
+ Interface for host program
+
+*/
+
+#define BYTE_PER_READ 512
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-----------------------------------------------------------------
+disc_Init
+Detects the inserted hardware and initialises it if necessary
+bool return OUT: true if a suitable device was found
+-----------------------------------------------------------------*/
+extern bool disc_Init(void) ;
+
+/*-----------------------------------------------------------------
+disc_IsInserted
+Is a usable disc inserted?
+bool return OUT: true if a disc is inserted
+-----------------------------------------------------------------*/
+extern bool disc_IsInserted(void) ;
+
+/*-----------------------------------------------------------------
+disc_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on disc to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_ReadSector(sector,buffer) disc_ReadSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on disc to write
+u8 numSecs IN: number of 512 byte sectors to write ,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_WriteSector(sector,buffer) disc_WriteSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_ClearStatus
+Tries to make the disc go back to idle mode
+bool return OUT: true if the disc is idle
+-----------------------------------------------------------------*/
+extern bool disc_ClearStatus(void) ;
+
+/*-----------------------------------------------------------------
+disc_Shutdown
+unload the disc interface
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_Shutdown(void) ;
+
+/*-----------------------------------------------------------------
+disc_HostType
+Returns a unique u32 number identifying the host type
+u32 return OUT: 0 if no host initialised, else the identifier of
+ the host
+-----------------------------------------------------------------*/
+extern u32 disc_HostType(void);
+
+/*-----------------------------------------------------------------
+disc_CacheFlush
+Flushes any cache writes to disc
+bool return OUT: true if successful, false if an error occurs
+Added by www.neoflash.com
+-----------------------------------------------------------------*/
+#ifdef DISC_CACHE
+extern bool disc_CacheFlush(void);
+#else
+static inline bool disc_CacheFlush(void)
+{
+ return true;
+}
+#endif // DISC_CACHE
+
+
+/*
+
+ Interface for IO libs
+
+*/
+
+#define FEATURE_MEDIUM_CANREAD 0x00000001
+#define FEATURE_MEDIUM_CANWRITE 0x00000002
+#define FEATURE_SLOT_GBA 0x00000010
+#define FEATURE_SLOT_NDS 0x00000020
+
+typedef bool (* FN_MEDIUM_STARTUP)(void) ;
+typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
+typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
+typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
+
+
+typedef struct {
+ unsigned long ul_ioType ;
+ unsigned long ul_Features ;
+ FN_MEDIUM_STARTUP fn_StartUp ;
+ FN_MEDIUM_ISINSERTED fn_IsInserted ;
+ FN_MEDIUM_READSECTORS fn_ReadSectors ;
+ FN_MEDIUM_WRITESECTORS fn_WriteSectors ;
+ FN_MEDIUM_CLEARSTATUS fn_ClearStatus ;
+ FN_MEDIUM_SHUTDOWN fn_Shutdown ;
+} IO_INTERFACE, *LPIO_INTERFACE ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // define DISC_IO_H
+#ifndef DISC_IO_H
+#define DISC_IO_H
+
+//----------------------------------------------------------------------
+// Customisable features
+
+// Use DMA to read the card, remove this line to use normal reads/writes
+// #define _CF_USE_DMA
+
+// Allow buffers not aligned to 16 bits when reading files.
+// Note that this will slow down access speed, so only use if you have to.
+// It is also incompatible with DMA
+//#define _CF_ALLOW_UNALIGNED
+
+// Device support options, added by www.neoflash.com
+
+#define SUPPORT_NMMC // comment out this line to remove Neoflash MK2 MMC Card support
+#define SUPPORT_MPCF // comment out this line to remove GBA Movie Player support
+#define SUPPORT_M3CF // comment out this line to remove M3 Perfect CF support
+#define SUPPORT_M3SD // comment out this line to remove M3 Perfect SD support
+#define SUPPORT_SCCF // comment out this line to remove Supercard CF support
+#define SUPPORT_SCSD // comment out this line to remove Supercard SD support
+//#define SUPPORT_EFA2 // comment out this line to remove EFA2 linker support
+#define SUPPORT_FCSR // comment out this line to remove GBA Flash Cart support
+
+// Disk caching options, added by www.neoflash.com
+// Each additional sector cache uses 512 bytes of memory
+// Disk caching is disabled on GBA to conserve memory
+
+#define DISC_CACHE // uncomment this line to enable disc caching
+#define DISC_CACHE_COUNT 16 // maximum number of sectors to cache (512 bytes per sector)
+//#define DISK_CACHE_DMA // use DMA for cache copies. If this is enabled, the data buffers must be word aligned
+
+
+//----------------------------------------------------------------------
+
+#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED
+ #error You can't use both DMA and unaligned memory
+#endif
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+// Disable NDS specific hardware and features if running on a GBA
+#ifndef NDS
+ #undef SUPPORT_NMMC
+ #undef DISC_CACHE
+#endif
+
+/*
+
+ Interface for host program
+
+*/
+
+#define BYTE_PER_READ 512
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-----------------------------------------------------------------
+disc_Init
+Detects the inserted hardware and initialises it if necessary
+bool return OUT: true if a suitable device was found
+-----------------------------------------------------------------*/
+extern bool disc_Init(void) ;
+
+/*-----------------------------------------------------------------
+disc_IsInserted
+Is a usable disc inserted?
+bool return OUT: true if a disc is inserted
+-----------------------------------------------------------------*/
+extern bool disc_IsInserted(void) ;
+
+/*-----------------------------------------------------------------
+disc_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on disc to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_ReadSector(sector,buffer) disc_ReadSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on disc to write
+u8 numSecs IN: number of 512 byte sectors to write ,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_WriteSector(sector,buffer) disc_WriteSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_ClearStatus
+Tries to make the disc go back to idle mode
+bool return OUT: true if the disc is idle
+-----------------------------------------------------------------*/
+extern bool disc_ClearStatus(void) ;
+
+/*-----------------------------------------------------------------
+disc_Shutdown
+unload the disc interface
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_Shutdown(void) ;
+
+/*-----------------------------------------------------------------
+disc_HostType
+Returns a unique u32 number identifying the host type
+u32 return OUT: 0 if no host initialised, else the identifier of
+ the host
+-----------------------------------------------------------------*/
+extern u32 disc_HostType(void);
+
+/*-----------------------------------------------------------------
+disc_CacheFlush
+Flushes any cache writes to disc
+bool return OUT: true if successful, false if an error occurs
+Added by www.neoflash.com
+-----------------------------------------------------------------*/
+#ifdef DISC_CACHE
+extern bool disc_CacheFlush(void);
+#else
+static inline bool disc_CacheFlush(void)
+{
+ return true;
+}
+#endif // DISC_CACHE
+
+
+/*
+
+ Interface for IO libs
+
+*/
+
+#define FEATURE_MEDIUM_CANREAD 0x00000001
+#define FEATURE_MEDIUM_CANWRITE 0x00000002
+#define FEATURE_SLOT_GBA 0x00000010
+#define FEATURE_SLOT_NDS 0x00000020
+
+typedef bool (* FN_MEDIUM_STARTUP)(void) ;
+typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
+typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
+typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
+
+
+typedef struct {
+ unsigned long ul_ioType ;
+ unsigned long ul_Features ;
+ FN_MEDIUM_STARTUP fn_StartUp ;
+ FN_MEDIUM_ISINSERTED fn_IsInserted ;
+ FN_MEDIUM_READSECTORS fn_ReadSectors ;
+ FN_MEDIUM_WRITESECTORS fn_WriteSectors ;
+ FN_MEDIUM_CLEARSTATUS fn_ClearStatus ;
+ FN_MEDIUM_SHUTDOWN fn_Shutdown ;
+} IO_INTERFACE, *LPIO_INTERFACE ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // define DISC_IO_H
diff --git a/backends/platform/ds/arm9/source/fat/gba_nds_fat.c b/backends/platform/ds/arm9/source/fat/gba_nds_fat.c
new file mode 100644
index 0000000000..b3b2858e41
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/gba_nds_fat.c
@@ -0,0 +1,6660 @@
+/*
+ gba_nds_fat.c
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+// Includes
+
+#include "gba_nds_fat.h"
+#include "disc_io.h"
+#include <string.h>
+#ifdef NDS
+ #include <nds/ipc.h> // Time on the NDS
+#endif
+//----------------------------------------------------------------
+// Data types
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//----------------------------------------------------------------
+// NDS memory access control register
+#ifdef NDS
+ #ifndef WAIT_CR
+ #define WAIT_CR (*(vu16*)0x04000204)
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// Appropriate placement of CF functions and data
+#ifdef NDS
+ #define _VARS_IN_RAM
+#else
+ #define _VARS_IN_RAM __attribute__ ((section (".sbss")))
+#endif
+
+
+//-----------------------------------------------------------------
+// FAT constants
+#define CLUSTER_EOF_16 0xFFFF
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FREE 0x0000
+#define CLUSTER_FIRST 0x0002
+
+#define FILE_LAST 0x00
+#define FILE_FREE 0xE5
+
+#define FAT16_ROOT_DIR_CLUSTER 0x00
+
+
+//-----------------------------------------------------------------
+// long file name constants
+#define LFN_END 0x40
+#define LFN_DEL 0x80
+
+//-----------------------------------------------------------------
+// Data Structures
+
+// Take care of packing for GCC - it doesn't obey pragma pack()
+// properly for ARM targets.
+#ifdef __GNUC__
+ #define __PACKED __attribute__ ((__packed__))
+#else
+ #define __PACKED
+ #pragma pack(1)
+#endif
+
+// Boot Sector - must be packed
+typedef struct
+{
+ u8 jmpBoot[3];
+ u8 OEMName[8];
+ // BIOS Parameter Block
+ u16 bytesPerSector;
+ u8 sectorsPerCluster;
+ u16 reservedSectors;
+ u8 numFATs;
+ u16 rootEntries;
+ u16 numSectorsSmall;
+ u8 mediaDesc;
+ u16 sectorsPerFAT;
+ u16 sectorsPerTrk;
+ u16 numHeads;
+ u32 numHiddenSectors;
+ u32 numSectors;
+ union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
+ {
+ struct
+ {
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[448];
+ } __PACKED fat16;
+ struct
+ {
+ // FAT32 extended block
+ u32 sectorsPerFAT32;
+ u16 extFlags;
+ u16 fsVer;
+ u32 rootClus;
+ u16 fsInfo;
+ u16 bkBootSec;
+ u8 reserved[12];
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[420];
+ } __PACKED fat32;
+ } __PACKED extBlock;
+
+ u16 bootSig;
+
+} __PACKED BOOT_SEC;
+
+// Directory entry - must be packed
+typedef struct
+{
+ u8 name[8];
+ u8 ext[3];
+ u8 attrib;
+ u8 reserved;
+ u8 cTime_ms;
+ u16 cTime;
+ u16 cDate;
+ u16 aDate;
+ u16 startClusterHigh;
+ u16 mTime;
+ u16 mDate;
+ u16 startCluster;
+ u32 fileSize;
+} __PACKED DIR_ENT;
+
+// Long file name directory entry - must be packed
+typedef struct
+{
+ u8 ordinal; // Position within LFN
+ u16 char0;
+ u16 char1;
+ u16 char2;
+ u16 char3;
+ u16 char4;
+ u8 flag; // Should be equal to ATTRIB_LFN
+ u8 reserved1; // Always 0x00
+ u8 checkSum; // Checksum of short file name (alias)
+ u16 char5;
+ u16 char6;
+ u16 char7;
+ u16 char8;
+ u16 char9;
+ u16 char10;
+ u16 reserved2; // Always 0x0000
+ u16 char11;
+ u16 char12;
+} __PACKED DIR_ENT_LFN;
+
+const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+// End of packed structs
+#ifdef __PACKED
+ #undef __PACKED
+#endif
+#ifndef __GNUC__
+ #pragma pack()
+#endif
+
+//-----------------------------------------------------------------
+// Global Variables
+
+// _VARS_IN_RAM variables are stored in the largest section of WRAM
+// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
+
+// Files
+_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN];
+
+// Long File names
+_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH];
+bool lfnExists;
+
+// Locations on card
+int filesysRootDir;
+int filesysRootDirClus;
+int filesysFAT;
+int filesysSecPerFAT;
+int filesysNumSec;
+int filesysData;
+int filesysBytePerSec;
+int filesysSecPerClus;
+int filesysBytePerClus;
+
+FS_TYPE filesysType = FS_UNKNOWN;
+u32 filesysTotalSize;
+
+// Info about FAT
+u32 fatLastCluster;
+u32 fatFirstFree;
+
+// fatBuffer used to reduce wear on the CF card from multiple writes
+_VARS_IN_RAM char fatBuffer[BYTE_PER_READ];
+u32 fatBufferCurSector;
+
+// Current working directory
+u32 curWorkDirCluster;
+
+// Position of the directory entry last retreived with FAT_GetDirEntry
+u32 wrkDirCluster;
+int wrkDirSector;
+int wrkDirOffset;
+
+// Global sector buffer to save on stack space
+_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ];
+
+//-----------------------------------------------------------------
+// Functions contained in this file - predeclarations
+char ucase (char character);
+u16 getRTCtoFileTime (void);
+u16 getRTCtoFileDate (void);
+
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry);
+bool FAT_ClearLinks (u32 cluster);
+DIR_ENT FAT_DirEntFromPath (const char* path);
+u32 FAT_FirstFreeCluster(void);
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin);
+u32 FAT_LinkFreeCluster(u32 cluster);
+u32 FAT_NextCluster(u32 cluster);
+bool FAT_WriteFatEntry (u32 cluster, u32 value);
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias);
+
+bool FAT_InitFiles (void);
+bool FAT_FreeFiles (void);
+int FAT_remove (const char* path);
+bool FAT_chdir (const char* path);
+FILE_TYPE FAT_FindFirstFile (char* filename);
+FILE_TYPE FAT_FindNextFile (char* filename);
+FILE_TYPE FAT_FileExists (const char* filename);
+bool FAT_GetAlias (char* alias);
+bool FAT_GetLongFilename (char* filename);
+u32 FAT_GetFileSize (void);
+u32 FAT_GetFileCluster (void);
+
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+bool FAT_fclose (FAT_FILE* file);
+bool FAT_feof(FAT_FILE* file);
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+u32 FAT_ftell (FAT_FILE* file);
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+char FAT_fgetc (FAT_FILE* file);
+char FAT_fputc (char c, FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+ucase
+Returns the uppercase version of the given char
+char IN: a character
+char return OUT: uppercase version of character
+-----------------------------------------------------------------*/
+char ucase (char character)
+{
+ if ((character > 0x60) && (character < 0x7B))
+ character = character - 0x20;
+ return (character);
+}
+
+
+/*-----------------------------------------------------------------
+getRTCtoFileTime and getRTCtoFileDate
+Returns the time / date in Dir Entry styled format
+u16 return OUT: time / date in Dir Entry styled format
+-----------------------------------------------------------------*/
+u16 getRTCtoFileTime (void)
+{
+#ifdef NDS
+ return (
+ ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) |
+ ( (IPC->rtc_minutes & 0x3F) << 5) |
+ ( (IPC->rtc_seconds >> 1) & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+u16 getRTCtoFileDate (void)
+{
+#ifdef NDS
+ return (
+ ( ((IPC->rtc_year + 20) & 0x7F) <<9) |
+ ( (IPC->rtc_month & 0xF) << 5) |
+ (IPC->rtc_day & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+
+/*-----------------------------------------------------------------
+Disc level FAT routines
+-----------------------------------------------------------------*/
+#define FAT_ClustToSect(m) \
+ (((m-2) * filesysSecPerClus) + filesysData)
+
+/*-----------------------------------------------------------------
+FAT_NextCluster
+Internal function - gets the cluster linked from input cluster
+-----------------------------------------------------------------*/
+u32 FAT_NextCluster(u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster = ((u8*)fatBuffer)[offset];
+ offset++;
+
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster |= (((u8*)fatBuffer)[offset]) << 8;
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ if (nextCluster >= 0x0FF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = ((u16*)fatBuffer)[offset];
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF;
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntry
+Internal function - writes FAT information about a cluster
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntry (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ {
+ return false;
+ }
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ return false;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ ((u16*)fatBuffer)[offset] = (value & 0xFFFF);
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ (((u32*)fatBuffer)[offset]) = value;
+
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+
+ return true;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ReadWriteFatEntryBuffered
+Internal function - writes FAT information about a cluster to a
+ buffer that should then be flushed to disc using
+ FAT_WriteFatEntryFlushBuffer()
+ Call FAT_WriteFatEntry first so as not to ruin the disc.
+ Also returns the entry being replaced
+-----------------------------------------------------------------*/
+u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+ u32 oldValue;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return CLUSTER_FREE;
+
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ oldValue = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0;
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ oldValue = ((u8*)fatBuffer)[offset] & 0xFF;
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ if (oldValue >= 0x0FF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u16*)fatBuffer)[offset];
+ ((u16*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0xFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u32*)fatBuffer)[offset];
+ ((u32*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0x0FFFFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ default:
+ oldValue = CLUSTER_FREE;
+ break;
+ }
+
+ return oldValue;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntryFlushBuffer
+Flush the FAT buffer back to the disc
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntryFlushBuffer (void)
+{
+ // write the buffer disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ {
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ return true;
+ } else {
+ return false;
+ }
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FirstFreeCluster
+Internal function - gets the first available free cluster
+-----------------------------------------------------------------*/
+u32 FAT_FirstFreeCluster(void)
+{
+ // Start at first valid cluster
+ if (fatFirstFree < CLUSTER_FIRST)
+ fatFirstFree = CLUSTER_FIRST;
+
+ while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster))
+ {
+ fatFirstFree++;
+ }
+ if (fatFirstFree > fatLastCluster)
+ {
+ return CLUSTER_EOF;
+ }
+ return fatFirstFree;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_LinkFreeCluster
+Internal function - gets the first available free cluster, sets it
+to end of file, links the input cluster to it then returns the
+cluster number
+-----------------------------------------------------------------*/
+u32 FAT_LinkFreeCluster(u32 cluster)
+{
+ u32 firstFree;
+ u32 curLink;
+
+ if (cluster > fatLastCluster)
+ {
+ return CLUSTER_FREE;
+ }
+
+ // Check if the cluster already has a link, and return it if so
+ curLink = FAT_NextCluster (cluster);
+ if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster))
+ {
+ return curLink; // Return the current link - don't allocate a new one
+ }
+
+ // Get a free cluster
+ firstFree = FAT_FirstFreeCluster();
+
+ // If couldn't get a free cluster then return
+ if (firstFree == CLUSTER_EOF)
+ {
+ return CLUSTER_FREE;
+ }
+
+ if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster))
+ {
+ // Update the linked from FAT entry
+ FAT_WriteFatEntry (cluster, firstFree);
+ }
+ // Create the linked to FAT entry
+ FAT_WriteFatEntry (firstFree, CLUSTER_EOF);
+
+ return firstFree;
+}
+#endif
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ClearLinks
+Internal function - frees any cluster used by a file
+-----------------------------------------------------------------*/
+bool FAT_ClearLinks (u32 cluster)
+{
+ u32 nextCluster;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return false;
+
+ // Store next cluster before erasing the link
+ nextCluster = FAT_NextCluster (cluster);
+
+ // Erase the link
+ FAT_WriteFatEntry (cluster, CLUSTER_FREE);
+
+ // Move onto next cluster
+ cluster = nextCluster;
+
+ while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE))
+ {
+ cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE);
+ }
+
+ // Flush fat write buffer
+ FAT_WriteFatEntryFlushBuffer ();
+
+ return true;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void)
+{
+ int i;
+ int bootSector;
+ BOOT_SEC* bootSec;
+
+ if (!disc_Init())
+ {
+ return (false);
+ }
+
+ // Read first sector of CF card
+ if ( !disc_ReadSector(0, globalBuffer)) {
+ return false;
+ }
+
+ // Make sure it is a valid MBR or boot sector
+ if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) {
+ return false;
+ }
+
+ // Check if there is a FAT string, which indicates this is a boot sector
+ if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
+ {
+ bootSector = 0;
+ }
+ // Check for FAT32
+ else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
+ {
+ bootSector = 0;
+ }
+ else // This is an MBR
+ {
+ // Find first valid partition from MBR
+ // First check for an active partition
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
+ // If it didn't find an active partition, search for any valid partition
+ if (i == 0x1FE)
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
+
+ // Go to first valid partition
+ if ( i != 0x1FE) // Make sure it found a partition
+ {
+ bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
+ } else {
+ bootSector = 0; // No partition found, assume this is a MBR free disk
+ }
+ }
+
+ // Read in boot sector
+ bootSec = (BOOT_SEC*) globalBuffer;
+ if (!disc_ReadSector (bootSector, bootSec)) {
+ return false;
+ }
+
+ // Store required information about the file system
+ if (bootSec->sectorsPerFAT != 0)
+ {
+ filesysSecPerFAT = bootSec->sectorsPerFAT;
+ }
+ else
+ {
+ filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
+ }
+
+ if (bootSec->numSectorsSmall != 0)
+ {
+ filesysNumSec = bootSec->numSectorsSmall;
+ }
+ else
+ {
+ filesysNumSec = bootSec->numSectors;
+ }
+
+ filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes
+ filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ;
+ filesysBytePerClus = filesysBytePerSec * filesysSecPerClus;
+ filesysFAT = bootSector + bootSec->reservedSectors;
+
+ filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT);
+ filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec);
+
+ filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec;
+
+ // Store info about FAT
+ fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster;
+ fatFirstFree = CLUSTER_FIRST;
+ fatBufferCurSector = 0;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+
+ if (fatLastCluster < 4085)
+ {
+ filesysType = FS_FAT12; // FAT12 volume - unsupported
+ }
+ else if (fatLastCluster < 65525)
+ {
+ filesysType = FS_FAT16; // FAT16 volume
+ }
+ else
+ {
+ filesysType = FS_FAT32; // FAT32 volume
+ }
+
+ if (filesysType != FS_FAT32)
+ {
+ filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER;
+ }
+ else // Set up for the FAT32 way
+ {
+ filesysRootDirClus = bootSec->extBlock.fat32.rootClus;
+ // Check if FAT mirroring is enabled
+ if (!(bootSec->extBlock.fat32.extFlags & 0x80))
+ {
+ // Use the active FAT
+ filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
+ }
+ }
+
+ // Set current directory to the root
+ curWorkDirCluster = filesysRootDirClus;
+ wrkDirCluster = filesysRootDirClus;
+ wrkDirSector = 0;
+ wrkDirOffset = 0;
+
+ // Set all files to free
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ openFiles[i].inUse = false;
+ }
+
+ // No long filenames so far
+ lfnExists = false;
+ for (i = 0; i < MAX_FILENAME_LENGTH; i++)
+ {
+ lfnName[i] = '\0';
+ }
+
+ return (true);
+}
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void)
+{
+ int i;
+
+ // Close all open files
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ if (openFiles[i].inUse == true)
+ {
+ FAT_fclose(&openFiles[i]);
+ }
+ }
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ // Clear card status
+ disc_ClearStatus();
+
+ // Return status of card
+ return disc_IsInserted();
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetDirEntry
+Return the file info structure of the next valid file entry
+u32 dirCluster: IN cluster of subdirectory table
+int entry: IN the desired file entry
+int origin IN: relative position of the entry
+DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if
+ the entry does not exist.
+-----------------------------------------------------------------*/
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin)
+{
+ DIR_ENT dir;
+ DIR_ENT_LFN lfn;
+ int firstSector = 0;
+ bool notFound = false;
+ bool found = false;
+ int maxSectors;
+ int lfnPos, aliasPos;
+ u8 lfnChkSum, chkSum;
+
+ int i;
+
+ dir.name[0] = FILE_FREE; // default to no file found
+ dir.attrib = 0x00;
+
+ // Check if fat has been initialised
+ if (filesysBytePerSec == 0)
+ {
+ return (dir);
+ }
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ break;
+ case SEEK_CUR: // Don't change anything
+ break;
+ case SEEK_END: // Find entry signifying end of directory
+ // Subtraction will never reach 0, so it keeps going
+ // until reaches end of directory
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ entry = -1;
+ break;
+ default:
+ return dir;
+ }
+
+ lfnChkSum = 0;
+ maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+
+ // Scan Dir for correct entry
+ firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster));
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ found = false;
+ notFound = false;
+ do {
+ wrkDirOffset++;
+ if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ wrkDirOffset = 0;
+ wrkDirSector++;
+ if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ wrkDirSector = 0;
+ wrkDirCluster = FAT_NextCluster(wrkDirCluster);
+ if (wrkDirCluster == CLUSTER_EOF)
+ {
+ notFound = true;
+ }
+ firstSector = FAT_ClustToSect(wrkDirCluster);
+ }
+ else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir)))
+ {
+ notFound = true; // Got to end of root dir
+ }
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ }
+ dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
+ if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL))
+ {
+ entry--;
+ if (lfnExists)
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 11; aliasPos++)
+ {
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]);
+ }
+ if (chkSum != lfnChkSum)
+ {
+ lfnExists = false;
+ lfnName[0] = '\0';
+ }
+ }
+ if (entry == 0)
+ {
+ if (!lfnExists)
+ {
+ FAT_GetFilename (dir, lfnName);
+ }
+ found = true;
+ }
+ }
+ else if (dir.name[0] == FILE_LAST)
+ {
+ if (origin == SEEK_END)
+ {
+ found = true;
+ }
+ else
+ {
+ notFound = true;
+ }
+ }
+ else if (dir.attrib == ATTRIB_LFN)
+ {
+ lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset];
+ if (lfn.ordinal & LFN_DEL)
+ {
+ lfnExists = false;
+ }
+ else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight)
+ {
+ lfnExists = true;
+ lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
+ lfnChkSum = lfn.checkSum;
+ }
+ if (lfnChkSum != lfn.checkSum)
+ {
+ lfnExists = false;
+ }
+ if (lfnExists)
+ {
+ lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13;
+ for (i = 0; i < 13; i++) {
+ lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/;
+ }
+ }
+ }
+ } while (!found && !notFound);
+
+ // If no file is found, return FILE_FREE
+ if (notFound)
+ {
+ dir.name[0] = FILE_FREE;
+ }
+
+ return (dir);
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile.
+ If a long name doesn't exist, it returns the short name
+ instead.
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename)
+{
+ if (filename == NULL)
+ return false;
+
+ strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1);
+ filename[MAX_FILENAME_LENGTH - 1] = '\0';
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetFilename
+Get the alias (short name) of the file or directory stored in
+ dirEntry
+DIR_ENT dirEntry: IN a valid directory table entry
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias)
+{
+ int i=0;
+ int j=0;
+
+ alias[0] = '\0';
+ if (dirEntry.name[0] != FILE_FREE)
+ {
+ if (dirEntry.name[0] == '.')
+ {
+ alias[0] = '.';
+ if (dirEntry.name[1] == '.')
+ {
+ alias[1] = '.';
+ alias[2] = '\0';
+ }
+ else
+ {
+ alias[1] = '\0';
+ }
+ }
+ else
+ {
+ // Copy the filename from the dirEntry to the string
+ for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++)
+ {
+ alias[i] = dirEntry.name[i];
+ }
+ // Copy the extension from the dirEntry to the string
+ if (dirEntry.ext[0] != ' ')
+ {
+ alias[i++] = '.';
+ for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++)
+ {
+ alias[i++] = dirEntry.ext[j];
+ }
+ }
+ alias[i] = '\0';
+ }
+ }
+
+ return (alias[0] != '\0');
+}
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias)
+{
+ if (alias == NULL)
+ {
+ return false;
+ }
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonLight
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask)
+{
+ // Get the file
+ if (!FAT_FileExists(filename)) {
+ return 0xff;
+ }
+
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes
+
+ disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate)
+{
+ struct tm timeInfo;
+
+ timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970
+ timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january
+ timeInfo.tm_mday = fileDate & 0x1f; // Day of the month
+
+ timeInfo.tm_hour = fileTime >> 11; // hours past midnight
+ timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour
+ timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute
+
+ return mktime(&timeInfo);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate);
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_DirEntFromPath
+Finds the directory entry for a file or directory from a path
+Path separator is a forward slash /
+const char* path: IN null terminated string of path.
+DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE
+ if the file was not found
+-----------------------------------------------------------------*/
+DIR_ENT FAT_DirEntFromPath (const char* path)
+{
+ int pathPos;
+ char name[MAX_FILENAME_LENGTH];
+ char alias[13];
+ int namePos;
+ bool found, notFound;
+ DIR_ENT dirEntry;
+ u32 dirCluster;
+ bool flagLFN, dotSeen;
+
+ // Start at beginning of path
+ pathPos = 0;
+
+ if (path[pathPos] == '/')
+ {
+ dirCluster = filesysRootDirClus; // Start at root directory
+ }
+ else
+ {
+ dirCluster = curWorkDirCluster; // Start at current working dir
+ }
+
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search until can't continue
+ found = false;
+ notFound = false;
+ while (!notFound && !found)
+ {
+ flagLFN = false;
+ // Copy name from path
+ namePos = 0;
+ if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) {
+ // Dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){
+ // Double dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ name[namePos++] = '.';
+ pathPos++;
+ } else {
+ // Copy name from path
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+ dotSeen = false;
+ while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/'))
+ {
+ name[namePos] = ucase(path[pathPos]);
+ if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (name[namePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ namePos++;
+ pathPos++;
+ }
+ // Check if a long filename was specified
+ if (namePos > 12)
+ {
+ flagLFN = true;
+ }
+ }
+
+ // Add end of string char
+ name[namePos] = '\0';
+
+ // Move through path to correct place
+ while ((path[pathPos] != '/') && (path[pathPos] != '\0'))
+ pathPos++;
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search current Dir for correct entry
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET);
+ while ( !found && !notFound)
+ {
+ // Match filename
+ found = true;
+ for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(lfnName[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (lfnName[namePos] == '\0'))
+ {
+ found = false;
+ }
+
+ // Check against alias as well.
+ if (!found)
+ {
+ FAT_GetFilename(dirEntry, alias);
+ found = true;
+ for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(alias[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (alias[namePos] == '\0'))
+ {
+ found = false;
+ }
+ }
+
+ if (dirEntry.name[0] == FILE_FREE)
+ // Couldn't find specified file
+ {
+ found = false;
+ notFound = true;
+ }
+ if (!found && !notFound)
+ {
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR);
+ }
+ }
+
+ if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0'))
+ // It has found a directory from within the path that needs to be followed
+ {
+ found = false;
+ dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ }
+ }
+
+ if (notFound)
+ {
+ dirEntry.name[0] = FILE_FREE;
+ dirEntry.attrib = 0x00;
+ }
+
+ return (dirEntry);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_AddDirEntry
+Creates a new dir entry for a file
+Path separator is a forward slash /
+const char* path: IN null terminated string of path to file.
+DIR_ENT newDirEntry IN: The directory entry to use.
+int file IN: The file being added (optional, use -1 if not used)
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry)
+{
+ char filename[MAX_FILENAME_LENGTH];
+ int filePos, pathPos, aliasPos;
+ char tempChar;
+ bool flagLFN, dotSeen;
+ char fileAlias[13] = {0};
+ int tailNum;
+
+ unsigned char chkSum = 0;
+
+ u32 oldWorkDirCluster;
+
+ DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer;
+ u32 dirCluster;
+ int secOffset;
+ int entryOffset;
+ int maxSectors;
+ u32 firstSector;
+
+ DIR_ENT_LFN lfnEntry;
+ int lfnPos = 0;
+
+ int dirEntryLength = 0;
+ int dirEntryRemain = 0;
+ u32 tempDirCluster;
+ int tempSecOffset;
+ int tempEntryOffset;
+ bool dirEndFlag = false;
+
+ int i;
+
+ // Store current working directory
+ oldWorkDirCluster = curWorkDirCluster;
+
+ // Find filename within path and change to correct directory
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+ flagLFN = false;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ filename[filePos] = '\0';
+ if (FAT_chdir(filename) == false)
+ {
+ curWorkDirCluster = oldWorkDirCluster;
+ return false; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ filename[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Skip over last slashes
+ while (path[pathPos] == '/')
+ pathPos++;
+
+ // Check if the filename has a leading "."
+ // If so, it is an LFN
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+
+ // Copy name from path
+ filePos = 0;
+ dotSeen = false;
+
+ while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0'))
+ {
+ filename[filePos] = path[pathPos];
+ if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (filename[filePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ filePos++;
+ pathPos++;
+ if ((filePos > 8) && !dotSeen) {
+ flagLFN = true;
+ }
+ }
+
+ if (filePos == 0) // No filename
+ {
+ return false;
+ }
+
+ // Check if a long filename was specified
+ if (filePos > 12)
+ {
+ flagLFN = true;
+ }
+
+ // Check if extension is > 3 characters long
+ if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) {
+ flagLFN = true;
+ }
+
+ lfnPos = (filePos - 1) / 13;
+
+ // Add end of string char
+ filename[filePos++] = '\0';
+ // Clear remaining chars
+ while (filePos < MAX_FILENAME_LENGTH)
+ filename[filePos++] = 0x01; // Set for LFN compatibility
+
+
+ if (flagLFN)
+ {
+ // Generate short filename - always a 2 digit number for tail
+ // Get first 5 chars of alias from LFN
+ aliasPos = 0;
+ filePos = 0;
+ if (filename[filePos] == '.') {
+ filePos++;
+ }
+ for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ // Pad Alias with underscores
+ while (aliasPos < 5)
+ fileAlias[aliasPos++] = '_';
+
+ fileAlias[5] = '~';
+ fileAlias[8] = '.';
+ fileAlias[9] = ' ';
+ fileAlias[10] = ' ';
+ fileAlias[11] = ' ';
+ if (strchr (filename, '.') != NULL) {
+ while(filename[filePos] != '\0')
+ {
+ filePos++;
+ if (filename[filePos] == '.')
+ {
+ pathPos = filePos;
+ }
+ }
+ filePos = pathPos + 1; //pathPos is used as a temporary variable
+ // Copy first 3 characters of extension
+ for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ } else {
+ aliasPos = 9;
+ }
+
+ // Pad Alias extension with spaces
+ while (aliasPos < 12)
+ fileAlias[aliasPos++] = ' ';
+
+ fileAlias[12] = '\0';
+
+
+ // Get a valid tail number
+ tailNum = 0;
+ do {
+ tailNum++;
+ fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit
+ fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit
+ } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100));
+
+ if (tailNum < 100) // Found an alias not being used
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 12; aliasPos++)
+ {
+ // Skip '.'
+ if (fileAlias[aliasPos] == '.')
+ aliasPos++;
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos];
+ }
+ }
+ else // Couldn't find a valid alias
+ {
+ return false;
+ }
+
+ dirEntryLength = lfnPos + 2;
+ }
+ else // Its not a long file name
+ {
+ // Just copy alias straight from filename
+ for (aliasPos = 0; aliasPos < 13; aliasPos++)
+ {
+ tempChar = ucase(filename[aliasPos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos] = tempChar;
+ }
+ fileAlias[12] = '\0';
+
+ lfnPos = -1;
+
+ dirEntryLength = 1;
+ }
+
+ // Change dirEntry name to match alias
+ for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++)
+ {
+ newDirEntry.name[aliasPos] = fileAlias[aliasPos];
+ }
+ while (aliasPos < 8)
+ {
+ newDirEntry.name[aliasPos++] = ' ';
+ }
+ aliasPos = 0;
+ while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0'))
+ aliasPos++;
+ filePos = 0;
+ while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0'))
+ {
+ tempChar = fileAlias[aliasPos++];
+ if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?')
+ newDirEntry.ext[filePos++] = tempChar;
+ }
+ while (filePos < 3)
+ {
+ newDirEntry.ext[filePos++] = ' ';
+ }
+
+ // Scan Dir for free entry
+ dirCluster = curWorkDirCluster;
+ secOffset = 0;
+ entryOffset = 0;
+ maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+ firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster));
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+
+ // Search for a large enough space to fit in new directory entry
+ while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0))
+ {
+
+ entryOffset++;
+
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ entryOffset = 0;
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) )
+ {
+ dirEntryRemain--;
+ } else {
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+ }
+ }
+
+ // Modifying the last directory is a special case - have to erase following entries
+ if (dirEntries[entryOffset].name[0] == FILE_LAST)
+ {
+ dirEndFlag = true;
+ }
+
+ // Recall last used entry
+ dirCluster = tempDirCluster;
+ secOffset = tempSecOffset;
+ entryOffset = tempEntryOffset;
+ dirEntryRemain = dirEntryLength;
+
+ // Re-read in first sector that will be written to
+ if (dirEndFlag && (entryOffset == 0)) {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ // Add new directory entry
+ while (dirEntryRemain > 0)
+ {
+ // Move to next entry, first pass advances from last used entry
+ entryOffset++;
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ // Write out the current sector if we need to
+ entryOffset = 0;
+ if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass
+ {
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+ }
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ if (dirEndFlag)
+ {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+ }
+
+ // Generate LFN entries
+ if (lfnPos >= 0)
+ {
+ lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0);
+ for (i = 0; i < 13; i++) {
+ if (filename [lfnPos * 13 + i] == 0x01) {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff;
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff;
+ } else {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i];
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00;
+ }
+ }
+
+ lfnEntry.checkSum = chkSum;
+ lfnEntry.flag = ATTRIB_LFN;
+ lfnEntry.reserved1 = 0;
+ lfnEntry.reserved2 = 0;
+
+ *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry;
+ lfnPos --;
+ lfnEntry.ordinal = 0;
+ } // end writing long filename entries
+ else
+ {
+ dirEntries[entryOffset] = newDirEntry;
+ if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) )
+ dirEntries[entryOffset+1].name[0] = FILE_LAST;
+ }
+
+ dirEntryRemain--;
+ }
+
+ // Write directory back to disk
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+
+ // Change back to Working DIR
+ curWorkDirCluster = oldWorkDirCluster;
+
+ return true;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile(char* filename)
+{
+ // Get the next directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile(char* filename)
+{
+ // Get the first directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindFirstFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindNextFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists(const char* filename)
+{
+ DIR_ENT dirEntry;
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (filename);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return FT_NONE;
+ }
+ else if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return FT_DIR;
+ }
+ else
+ {
+ return FT_FILE;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void)
+{
+ return filesysType;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void)
+{
+ return filesysTotalSize;
+}
+
+
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path)
+{
+ DIR_ENT dir;
+ if (path[0] == '/' && path[1] == '\0')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ return true;
+ }
+ if (path[0] == '\0') // Return true if changing relative to nothing
+ {
+ return true;
+ }
+
+ dir = FAT_DirEntFromPath (path);
+
+ if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE))
+ {
+ // Change directory
+ curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16);
+
+ // Move to correct cluster for root directory
+ if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER)
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ // Reset file position in directory
+ wrkDirCluster = curWorkDirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ return true;
+ }
+ else
+ {
+ // Couldn't change directory - wrong path specified
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns NULL if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode)
+{
+ int fileNum;
+ FAT_FILE* file;
+ DIR_ENT dirEntry;
+#ifdef CAN_WRITE_TO_DISC
+ u32 startCluster;
+ int clusCount;
+#endif
+
+ char* pchTemp;
+ // Check that a valid mode was specified
+ pchTemp = strpbrk ( mode, "rRwWaA" );
+ if (pchTemp == NULL)
+ {
+ return NULL;
+ }
+ if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL)
+ {
+ return NULL;
+ }
+
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Check that it is not a directory
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return NULL;
+ }
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check that it is not a read only file being openned in a writing mode
+ if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO))
+ {
+ return NULL;
+ }
+#else
+ if ( (strpbrk(mode, "wWaA+") != NULL))
+ {
+ return NULL;
+ }
+#endif
+
+ // Find a free file buffer
+ for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++);
+
+ if (fileNum == MAX_FILES_OPEN) // No free files
+ {
+ return NULL;
+ }
+
+ file = &openFiles[fileNum];
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+
+ if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // File must exist
+ {
+ return NULL;
+ }
+
+ file->read = true;
+#ifdef CAN_WRITE_TO_DISC
+ file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+#else
+ file->write = false;
+#endif
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check if file is openned for random. If it is, and currently has no cluster, one must be
+ // assigned to it.
+ if (file->write && file->firstCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+#endif
+
+ file->length = dirEntry.fileSize;
+ file->curPos = 0;
+ file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ } // mode "r"
+
+#ifdef CAN_WRITE_TO_DISC
+ if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ }
+ else // Already a file entry
+ {
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+ }
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (dirEntry.name[0] == FILE_FREE) // No file
+ {
+ // Have to create a new entry
+ if(!FAT_AddDirEntry (path, dirEntry))
+ {
+ return NULL;
+ }
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // Already a file
+ {
+ // Just modify the old entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+ file->write = true;
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = 0; // Should always have 0 bytes if openning in "w" mode
+ file->curPos = 0;
+ file->curClus = startCluster;
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ // Empty file, so empty read buffer
+ memset (file->readBuffer, 0, BYTE_PER_READ);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ }
+
+ if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+
+ // The file has no data in it
+ dirEntry.fileSize = 0;
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ if(!FAT_AddDirEntry (path, dirEntry))
+ return NULL;
+
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // File already exists - reuse the old directory entry
+ {
+ startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ // If it currently has no cluster, one must be assigned to it.
+ if (startCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ } else {
+
+ // Follow cluster list until last one is found
+ clusCount = dirEntry.fileSize / filesysBytePerClus;
+ file->appClus = startCluster;
+ while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF))
+ {
+ file->appClus = FAT_NextCluster (file->appClus);
+ }
+ if (clusCount >= 0) // Check if ran out of clusters
+ {
+ // Set flag to allocate new cluster when needed
+ file->appSect = filesysSecPerClus;
+ file->appByte = 0;
+ }
+ }
+ }
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL );
+ file->write = false;
+ file->append = true;
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ;
+ file->appByte = dirEntry.fileSize % BYTE_PER_READ;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = dirEntry.fileSize;
+ file->curPos = dirEntry.fileSize;
+ file->curClus = file->appClus;
+ file->curSect = file->appSect;
+ file->curByte = file->appByte;
+
+ // Read into buffer
+ disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer);
+ file->inUse = true; // We're using this file now
+ return file;
+ }
+#endif
+
+ // Can only reach here if a bad mode was specified
+ return NULL;
+}
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file)
+{
+ // Clear memory used by file information
+ if ((file != NULL) && (file->inUse == true))
+ {
+#ifdef CAN_WRITE_TO_DISC
+ if (file->write || file->append)
+ {
+ // Write new length, time and date back to directory entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length;
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate();
+
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ }
+#endif
+ file->inUse = false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file)
+{
+ // Return the position as specified in the FAT_FILE structure
+ if ((file != NULL) && (file->inUse == true))
+ {
+ return file->curPos;
+ }
+ else
+ {
+ // Return -1 if no file was given
+ return -1;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+FAT_FILE* file: IN handle of an open file
+s32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin)
+{
+ u32 cluster, nextCluster;
+ int clusCount;
+ u32 position;
+ u32 curPos;
+
+ if ((file == NULL) || (file->inUse == false)) // invalid file
+ {
+ return -1;
+ }
+
+ // Can't seek in append only mode
+ if (!file->read && !file->write)
+ {
+ return -1;
+ }
+
+ curPos = file->curPos;
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ if (offset >= 0)
+ {
+ position = offset;
+ } else {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ break;
+ case SEEK_CUR:
+ if (offset >= 0)
+ {
+ position = curPos + offset;
+ }
+ else if ( (u32)(offset * -1) >= curPos )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = curPos - (u32)(offset * -1);
+ }
+ break;
+ case SEEK_END:
+ if (offset >= 0)
+ {
+ // Seeking to end of file
+ position = file->length; // Fixed thanks to MoonLight
+ }
+ else if ( (u32)(offset * -1) >= file->length )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = file->length - (u32)(offset * -1);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (position > file->length)
+ {
+ // Tried to go past end of file
+ position = file->length;
+ }
+
+ // Save position
+ file->curPos = position;
+
+
+ // Calculate where the correct cluster is
+ if (position > curPos)
+ {
+ clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ
+ cluster = file->curClus;
+ } else {
+ clusCount = position / filesysBytePerClus;
+ cluster = file->firstCluster;
+ }
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ;
+ file->curByte = position % BYTE_PER_READ;
+
+ // Follow cluster list until desired one is found
+ if (clusCount > 0) // Only look at next cluster if need to
+ {
+ nextCluster = FAT_NextCluster (cluster);
+ } else {
+ nextCluster = cluster;
+ }
+ while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF))
+ {
+ cluster = nextCluster;
+ nextCluster = FAT_NextCluster (cluster);
+ }
+ // Check if ran out of clusters, and the file is being written to
+ if ((clusCount >= 0) && (file->write || file->append))
+ {
+ // Set flag to allocate a new cluster
+ file->curSect = filesysSecPerClus;
+ file->curByte = 0;
+ }
+ file->curClus = cluster;
+
+ // Reload sector buffer for new position in file, if it is a different sector
+ if ((curPos ^ position) >= BYTE_PER_READ)
+ {
+ disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer);
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in size * count bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+ u32 tempNextCluster;
+
+ int tempVar;
+
+ char* data = (char*)buffer;
+
+ u32 length = size * count;
+ u32 remain;
+
+ bool flagNoError = true;
+
+ // Can't read non-existant files
+ if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL)
+ return 0;
+
+ // Can only read files openned for reading
+ if (!file->read)
+ return 0;
+
+ // Don't read past end of file
+ if (length + file->curPos > file->length)
+ length = file->length - file->curPos;
+
+ remain = length;
+
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(data, &(file->readBuffer[curByte]), tempVar);
+ remain -= tempVar;
+ data += tempVar;
+
+ curByte += tempVar;
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // align to cluster
+ // tempVar is number of sectors to read
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+
+ curSect += tempVar;
+ }
+
+ // Move onto next cluster
+ // It should get to here without reading anything if a cluster is due to be allocated
+ if (curSect >= filesysSecPerClus)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read in whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+
+ // Advance to next cluster
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer);
+ if (remain > 0)
+ {
+ memcpy(data, file->readBuffer, remain);
+ curByte += remain;
+ remain = 0;
+ }
+ }
+
+ // Length read is the wanted length minus the stuff not read
+ length = length - remain;
+
+ // Update file information
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ return length;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+
+ u32 tempNextCluster;
+ int tempVar;
+ u32 length = size * count;
+ u32 remain = length;
+ char* data = (char*)buffer;
+
+ char* writeBuffer;
+
+ bool flagNoError = true;
+ bool flagAppending = false;
+
+ if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL)
+ return 0;
+
+ if (file->write)
+ {
+ // Write at current read pointer
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Use read buffer as write buffer
+ writeBuffer = file->readBuffer;
+
+ // If it is writing past the current end of file, set appending flag
+ if (length + file->curPos > file->length)
+ {
+ flagAppending = true;
+ }
+ }
+ else if (file->append)
+ {
+ // Write at end of file
+ curByte = file->appByte;
+ curSect = file->appSect;
+ curClus = file->appClus;
+ flagAppending = true;
+
+ // Use global buffer as write buffer, don't touch read buffer
+ writeBuffer = (char*)globalBuffer;
+ disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer);
+ }
+ else
+ {
+ return 0;
+ }
+
+ // Move onto next cluster if needed
+ if (curSect >= filesysSecPerClus)
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ memset(writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ curClus = tempNextCluster;
+ disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(&(writeBuffer[curByte]), data, tempVar);
+ remain -= tempVar;
+ data += tempVar;
+ curByte += tempVar;
+
+ // Write buffer back to disk
+ disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer);
+
+ // Move onto next sector
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // Align to cluster
+ // tempVar is number of sectors to write
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0))
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ }
+
+ // Write whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+ if (remain > 0)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ break;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ } else {
+ // Allocate a new cluster when next writing the file
+ curSect = filesysSecPerClus;
+ }
+ }
+
+ // Write remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ if (flagAppending)
+ {
+ // Zero sector before using it
+ memset (writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ // Modify existing sector
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ if (remain > 0) {
+ memcpy(writeBuffer, data, remain);
+ curByte += remain;
+ remain = 0;
+ disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Amount read is the originally requested amount minus stuff remaining
+ length = length - remain;
+
+ // Update file information
+ if (file->write) // Writing also shifts the read pointer
+ {
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ if (file->length < file->curPos)
+ {
+ file->length = file->curPos;
+ }
+ }
+ else if (file->append) // Appending doesn't affect the read pointer
+ {
+ file->appByte = curByte;
+ file->appSect = curSect;
+ file->appClus = curClus;
+ file->length = file->length + length;
+ }
+
+ return length;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file)
+{
+ if ((file == NULL) || (file->inUse == false))
+ return true; // Return eof on invalid files
+
+ return (file->length == file->curPos);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path)
+{
+ DIR_ENT dirEntry;
+ u32 oldWorkDirCluster;
+ char checkFilename[13];
+ FILE_TYPE checkFiletype;
+
+ dirEntry = FAT_DirEntFromPath (path);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return -1;
+ }
+
+ // Only delete directories if the directory is entry
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ // Change to the directory temporarily
+ oldWorkDirCluster = curWorkDirCluster;
+ FAT_chdir(path);
+
+ // Search for files or directories, excluding the . and .. entries
+ checkFiletype = FAT_FindFirstFile (checkFilename);
+ while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE))
+ {
+ checkFiletype = FAT_FindNextFile (checkFilename);
+ }
+
+ // Change back to working directory
+ curWorkDirCluster = oldWorkDirCluster;
+
+ // Check that the directory is empty
+ if (checkFiletype != FT_NONE)
+ {
+ // Directory isn't empty
+ return -1;
+ }
+ }
+
+ // Refresh directory information
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+
+ // Remove Directory entry
+ disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE;
+ disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ return 0;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path)
+{
+ u32 newDirCluster;
+ u32 parentDirCluster;
+ DIR_ENT dirEntry;
+ DIR_ENT* entries = (DIR_ENT*)globalBuffer;
+ int i;
+
+ int pathPos, filePos;
+ char pathname[MAX_FILENAME_LENGTH];
+ u32 oldDirCluster;
+
+ if (FAT_FileExists(path) != FT_NONE)
+ {
+ return -1; // File or directory exists with that name
+ }
+
+ // Find filename within path and change to that directory
+ oldDirCluster = curWorkDirCluster;
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ pathname[filePos] = '\0';
+ if (FAT_chdir(pathname) == false)
+ {
+ curWorkDirCluster = oldDirCluster;
+ return -1; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ pathname[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Now grab the parent directory's cluster
+ parentDirCluster = curWorkDirCluster;
+ curWorkDirCluster = oldDirCluster;
+
+ // Get a new cluster for the file
+ newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE);
+
+ if (newDirCluster == CLUSTER_FREE)
+ {
+ return -1; // Couldn't get a new cluster for the directory
+ }
+ // Fill in directory entry's information
+ dirEntry.attrib = ATTRIB_DIR;
+ dirEntry.reserved = 0;
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (newDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF);
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (FAT_AddDirEntry (path, dirEntry) == false)
+ {
+ return -1; // Couldn't add the directory entry
+ }
+
+ // Create the new directory itself
+ memset(entries, FILE_LAST, BYTE_PER_READ);
+
+ // Create . directory entry
+ dirEntry.name[0] = '.';
+ // Fill name and extension with spaces
+ for (i = 1; i < 11; i++)
+ {
+ dirEntry.name[i] = ' ';
+ }
+
+ memcpy(entries, &dirEntry, sizeof(dirEntry));
+
+ // Create .. directory entry
+ dirEntry.name[1] = '.';
+ dirEntry.startCluster = (parentDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF);
+
+ memcpy(&entries[1], &dirEntry, sizeof(dirEntry));
+
+ // Write entry to disc
+ disc_WriteSector(FAT_ClustToSect(newDirCluster), entries);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ return 0;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file)
+{
+ char c;
+ return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file)
+{
+ return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file)
+{
+ u32 curPos;
+ u32 readLength;
+ char *returnChar;
+
+ // invalid filehandle
+ if (file == NULL)
+ {
+ return NULL ;
+ }
+
+ // end of file
+ if (FAT_feof(file)==true)
+ {
+ return NULL ;
+ }
+
+ // save current position
+ curPos = FAT_ftell(file);
+
+ // read the full buffer (max string chars is num-1 and one end of string \0
+ readLength = FAT_fread(tgtBuffer,1,num-1,file) ;
+
+ // mark least possible end of string
+ tgtBuffer[readLength] = '\0' ;
+
+ if (readLength==0) {
+ // return error
+ return NULL ;
+ }
+
+ // get position of first return '\r'
+ returnChar = strchr(tgtBuffer,'\r');
+
+ // if no return is found, search for a newline
+ if (returnChar == NULL)
+ {
+ returnChar = strchr(tgtBuffer,'\n');
+ }
+
+ // Mark the return, if existant, as end of line/string
+ if (returnChar!=NULL) {
+ *returnChar++ = 0 ;
+ if (*returnChar=='\n') { // catch newline too when jumping over the end
+ // return to location after \r\n (strlen+2)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ;
+ return tgtBuffer ;
+ } else {
+ // return to location after \r (strlen+1)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ;
+ return tgtBuffer ;
+ }
+ }
+
+ return tgtBuffer ;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file)
+{
+ u32 writtenBytes;
+ // save string except end of string '\0'
+ writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file);
+
+ // check if we had an error
+ if (writtenBytes != strlen(string))
+ {
+ // return EOF error
+ return EOF;
+ }
+
+ // return the charcount written
+ return writtenBytes ;
+}
+#endif
+
+
+
+/*
+ gba_nds_fat.c
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+// Includes
+
+#include "gba_nds_fat.h"
+#include "disc_io.h"
+#include <string.h>
+#ifdef NDS
+ #include <nds/ipc.h> // Time on the NDS
+#endif
+//----------------------------------------------------------------
+// Data types
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//----------------------------------------------------------------
+// NDS memory access control register
+#ifdef NDS
+ #ifndef WAIT_CR
+ #define WAIT_CR (*(vu16*)0x04000204)
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// Appropriate placement of CF functions and data
+#ifdef NDS
+ #define _VARS_IN_RAM
+#else
+ #define _VARS_IN_RAM __attribute__ ((section (".sbss")))
+#endif
+
+
+//-----------------------------------------------------------------
+// FAT constants
+#define CLUSTER_EOF_16 0xFFFF
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FREE 0x0000
+#define CLUSTER_FIRST 0x0002
+
+#define FILE_LAST 0x00
+#define FILE_FREE 0xE5
+
+#define FAT16_ROOT_DIR_CLUSTER 0x00
+
+
+//-----------------------------------------------------------------
+// long file name constants
+#define LFN_END 0x40
+#define LFN_DEL 0x80
+
+//-----------------------------------------------------------------
+// Data Structures
+
+// Take care of packing for GCC - it doesn't obey pragma pack()
+// properly for ARM targets.
+#ifdef __GNUC__
+ #define __PACKED __attribute__ ((__packed__))
+#else
+ #define __PACKED
+ #pragma pack(1)
+#endif
+
+// Boot Sector - must be packed
+typedef struct
+{
+ u8 jmpBoot[3];
+ u8 OEMName[8];
+ // BIOS Parameter Block
+ u16 bytesPerSector;
+ u8 sectorsPerCluster;
+ u16 reservedSectors;
+ u8 numFATs;
+ u16 rootEntries;
+ u16 numSectorsSmall;
+ u8 mediaDesc;
+ u16 sectorsPerFAT;
+ u16 sectorsPerTrk;
+ u16 numHeads;
+ u32 numHiddenSectors;
+ u32 numSectors;
+ union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
+ {
+ struct
+ {
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[448];
+ } __PACKED fat16;
+ struct
+ {
+ // FAT32 extended block
+ u32 sectorsPerFAT32;
+ u16 extFlags;
+ u16 fsVer;
+ u32 rootClus;
+ u16 fsInfo;
+ u16 bkBootSec;
+ u8 reserved[12];
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[420];
+ } __PACKED fat32;
+ } __PACKED extBlock;
+
+ u16 bootSig;
+
+} __PACKED BOOT_SEC;
+
+// Directory entry - must be packed
+typedef struct
+{
+ u8 name[8];
+ u8 ext[3];
+ u8 attrib;
+ u8 reserved;
+ u8 cTime_ms;
+ u16 cTime;
+ u16 cDate;
+ u16 aDate;
+ u16 startClusterHigh;
+ u16 mTime;
+ u16 mDate;
+ u16 startCluster;
+ u32 fileSize;
+} __PACKED DIR_ENT;
+
+// Long file name directory entry - must be packed
+typedef struct
+{
+ u8 ordinal; // Position within LFN
+ u16 char0;
+ u16 char1;
+ u16 char2;
+ u16 char3;
+ u16 char4;
+ u8 flag; // Should be equal to ATTRIB_LFN
+ u8 reserved1; // Always 0x00
+ u8 checkSum; // Checksum of short file name (alias)
+ u16 char5;
+ u16 char6;
+ u16 char7;
+ u16 char8;
+ u16 char9;
+ u16 char10;
+ u16 reserved2; // Always 0x0000
+ u16 char11;
+ u16 char12;
+} __PACKED DIR_ENT_LFN;
+
+const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+// End of packed structs
+#ifdef __PACKED
+ #undef __PACKED
+#endif
+#ifndef __GNUC__
+ #pragma pack()
+#endif
+
+//-----------------------------------------------------------------
+// Global Variables
+
+// _VARS_IN_RAM variables are stored in the largest section of WRAM
+// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
+
+// Files
+_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN];
+
+// Long File names
+_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH];
+bool lfnExists;
+
+// Locations on card
+int filesysRootDir;
+int filesysRootDirClus;
+int filesysFAT;
+int filesysSecPerFAT;
+int filesysNumSec;
+int filesysData;
+int filesysBytePerSec;
+int filesysSecPerClus;
+int filesysBytePerClus;
+
+FS_TYPE filesysType = FS_UNKNOWN;
+u32 filesysTotalSize;
+
+// Info about FAT
+u32 fatLastCluster;
+u32 fatFirstFree;
+
+// fatBuffer used to reduce wear on the CF card from multiple writes
+_VARS_IN_RAM char fatBuffer[BYTE_PER_READ];
+u32 fatBufferCurSector;
+
+// Current working directory
+u32 curWorkDirCluster;
+
+// Position of the directory entry last retreived with FAT_GetDirEntry
+u32 wrkDirCluster;
+int wrkDirSector;
+int wrkDirOffset;
+
+// Global sector buffer to save on stack space
+_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ];
+
+//-----------------------------------------------------------------
+// Functions contained in this file - predeclarations
+char ucase (char character);
+u16 getRTCtoFileTime (void);
+u16 getRTCtoFileDate (void);
+
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry);
+bool FAT_ClearLinks (u32 cluster);
+DIR_ENT FAT_DirEntFromPath (const char* path);
+u32 FAT_FirstFreeCluster(void);
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin);
+u32 FAT_LinkFreeCluster(u32 cluster);
+u32 FAT_NextCluster(u32 cluster);
+bool FAT_WriteFatEntry (u32 cluster, u32 value);
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias);
+
+bool FAT_InitFiles (void);
+bool FAT_FreeFiles (void);
+int FAT_remove (const char* path);
+bool FAT_chdir (const char* path);
+FILE_TYPE FAT_FindFirstFile (char* filename);
+FILE_TYPE FAT_FindNextFile (char* filename);
+FILE_TYPE FAT_FileExists (const char* filename);
+bool FAT_GetAlias (char* alias);
+bool FAT_GetLongFilename (char* filename);
+u32 FAT_GetFileSize (void);
+u32 FAT_GetFileCluster (void);
+
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+bool FAT_fclose (FAT_FILE* file);
+bool FAT_feof(FAT_FILE* file);
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+u32 FAT_ftell (FAT_FILE* file);
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+char FAT_fgetc (FAT_FILE* file);
+char FAT_fputc (char c, FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+ucase
+Returns the uppercase version of the given char
+char IN: a character
+char return OUT: uppercase version of character
+-----------------------------------------------------------------*/
+char ucase (char character)
+{
+ if ((character > 0x60) && (character < 0x7B))
+ character = character - 0x20;
+ return (character);
+}
+
+
+/*-----------------------------------------------------------------
+getRTCtoFileTime and getRTCtoFileDate
+Returns the time / date in Dir Entry styled format
+u16 return OUT: time / date in Dir Entry styled format
+-----------------------------------------------------------------*/
+u16 getRTCtoFileTime (void)
+{
+#ifdef NDS
+ return (
+ ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) |
+ ( (IPC->rtc_minutes & 0x3F) << 5) |
+ ( (IPC->rtc_seconds >> 1) & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+u16 getRTCtoFileDate (void)
+{
+#ifdef NDS
+ return (
+ ( ((IPC->rtc_year + 20) & 0x7F) <<9) |
+ ( (IPC->rtc_month & 0xF) << 5) |
+ (IPC->rtc_day & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+
+/*-----------------------------------------------------------------
+Disc level FAT routines
+-----------------------------------------------------------------*/
+#define FAT_ClustToSect(m) \
+ (((m-2) * filesysSecPerClus) + filesysData)
+
+/*-----------------------------------------------------------------
+FAT_NextCluster
+Internal function - gets the cluster linked from input cluster
+-----------------------------------------------------------------*/
+u32 FAT_NextCluster(u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster = ((u8*)fatBuffer)[offset];
+ offset++;
+
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster |= (((u8*)fatBuffer)[offset]) << 8;
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ if (nextCluster >= 0x0FF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = ((u16*)fatBuffer)[offset];
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF;
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntry
+Internal function - writes FAT information about a cluster
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntry (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ {
+ return false;
+ }
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ return false;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ ((u16*)fatBuffer)[offset] = (value & 0xFFFF);
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ (((u32*)fatBuffer)[offset]) = value;
+
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+
+ return true;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ReadWriteFatEntryBuffered
+Internal function - writes FAT information about a cluster to a
+ buffer that should then be flushed to disc using
+ FAT_WriteFatEntryFlushBuffer()
+ Call FAT_WriteFatEntry first so as not to ruin the disc.
+ Also returns the entry being replaced
+-----------------------------------------------------------------*/
+u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+ u32 oldValue;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return CLUSTER_FREE;
+
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ oldValue = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0;
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ oldValue = ((u8*)fatBuffer)[offset] & 0xFF;
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ if (oldValue >= 0x0FF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u16*)fatBuffer)[offset];
+ ((u16*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0xFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u32*)fatBuffer)[offset];
+ ((u32*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0x0FFFFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ default:
+ oldValue = CLUSTER_FREE;
+ break;
+ }
+
+ return oldValue;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntryFlushBuffer
+Flush the FAT buffer back to the disc
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntryFlushBuffer (void)
+{
+ // write the buffer disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ {
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ return true;
+ } else {
+ return false;
+ }
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FirstFreeCluster
+Internal function - gets the first available free cluster
+-----------------------------------------------------------------*/
+u32 FAT_FirstFreeCluster(void)
+{
+ // Start at first valid cluster
+ if (fatFirstFree < CLUSTER_FIRST)
+ fatFirstFree = CLUSTER_FIRST;
+
+ while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster))
+ {
+ fatFirstFree++;
+ }
+ if (fatFirstFree > fatLastCluster)
+ {
+ return CLUSTER_EOF;
+ }
+ return fatFirstFree;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_LinkFreeCluster
+Internal function - gets the first available free cluster, sets it
+to end of file, links the input cluster to it then returns the
+cluster number
+-----------------------------------------------------------------*/
+u32 FAT_LinkFreeCluster(u32 cluster)
+{
+ u32 firstFree;
+ u32 curLink;
+
+ if (cluster > fatLastCluster)
+ {
+ return CLUSTER_FREE;
+ }
+
+ // Check if the cluster already has a link, and return it if so
+ curLink = FAT_NextCluster (cluster);
+ if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster))
+ {
+ return curLink; // Return the current link - don't allocate a new one
+ }
+
+ // Get a free cluster
+ firstFree = FAT_FirstFreeCluster();
+
+ // If couldn't get a free cluster then return
+ if (firstFree == CLUSTER_EOF)
+ {
+ return CLUSTER_FREE;
+ }
+
+ if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster))
+ {
+ // Update the linked from FAT entry
+ FAT_WriteFatEntry (cluster, firstFree);
+ }
+ // Create the linked to FAT entry
+ FAT_WriteFatEntry (firstFree, CLUSTER_EOF);
+
+ return firstFree;
+}
+#endif
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ClearLinks
+Internal function - frees any cluster used by a file
+-----------------------------------------------------------------*/
+bool FAT_ClearLinks (u32 cluster)
+{
+ u32 nextCluster;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return false;
+
+ // Store next cluster before erasing the link
+ nextCluster = FAT_NextCluster (cluster);
+
+ // Erase the link
+ FAT_WriteFatEntry (cluster, CLUSTER_FREE);
+
+ // Move onto next cluster
+ cluster = nextCluster;
+
+ while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE))
+ {
+ cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE);
+ }
+
+ // Flush fat write buffer
+ FAT_WriteFatEntryFlushBuffer ();
+
+ return true;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void)
+{
+ int i;
+ int bootSector;
+ BOOT_SEC* bootSec;
+
+ if (!disc_Init())
+ {
+ return (false);
+ }
+
+ // Read first sector of CF card
+ if ( !disc_ReadSector(0, globalBuffer)) {
+ return false;
+ }
+
+ // Make sure it is a valid MBR or boot sector
+ if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) {
+ return false;
+ }
+
+ // Check if there is a FAT string, which indicates this is a boot sector
+ if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
+ {
+ bootSector = 0;
+ }
+ // Check for FAT32
+ else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
+ {
+ bootSector = 0;
+ }
+ else // This is an MBR
+ {
+ // Find first valid partition from MBR
+ // First check for an active partition
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
+ // If it didn't find an active partition, search for any valid partition
+ if (i == 0x1FE)
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
+
+ // Go to first valid partition
+ if ( i != 0x1FE) // Make sure it found a partition
+ {
+ bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
+ } else {
+ bootSector = 0; // No partition found, assume this is a MBR free disk
+ }
+ }
+
+ // Read in boot sector
+ bootSec = (BOOT_SEC*) globalBuffer;
+ if (!disc_ReadSector (bootSector, bootSec)) {
+ return false;
+ }
+
+ // Store required information about the file system
+ if (bootSec->sectorsPerFAT != 0)
+ {
+ filesysSecPerFAT = bootSec->sectorsPerFAT;
+ }
+ else
+ {
+ filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
+ }
+
+ if (bootSec->numSectorsSmall != 0)
+ {
+ filesysNumSec = bootSec->numSectorsSmall;
+ }
+ else
+ {
+ filesysNumSec = bootSec->numSectors;
+ }
+
+ filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes
+ filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ;
+ filesysBytePerClus = filesysBytePerSec * filesysSecPerClus;
+ filesysFAT = bootSector + bootSec->reservedSectors;
+
+ filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT);
+ filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec);
+
+ filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec;
+
+ // Store info about FAT
+ fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster;
+ fatFirstFree = CLUSTER_FIRST;
+ fatBufferCurSector = 0;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+
+ if (fatLastCluster < 4085)
+ {
+ filesysType = FS_FAT12; // FAT12 volume - unsupported
+ }
+ else if (fatLastCluster < 65525)
+ {
+ filesysType = FS_FAT16; // FAT16 volume
+ }
+ else
+ {
+ filesysType = FS_FAT32; // FAT32 volume
+ }
+
+ if (filesysType != FS_FAT32)
+ {
+ filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER;
+ }
+ else // Set up for the FAT32 way
+ {
+ filesysRootDirClus = bootSec->extBlock.fat32.rootClus;
+ // Check if FAT mirroring is enabled
+ if (!(bootSec->extBlock.fat32.extFlags & 0x80))
+ {
+ // Use the active FAT
+ filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
+ }
+ }
+
+ // Set current directory to the root
+ curWorkDirCluster = filesysRootDirClus;
+ wrkDirCluster = filesysRootDirClus;
+ wrkDirSector = 0;
+ wrkDirOffset = 0;
+
+ // Set all files to free
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ openFiles[i].inUse = false;
+ }
+
+ // No long filenames so far
+ lfnExists = false;
+ for (i = 0; i < MAX_FILENAME_LENGTH; i++)
+ {
+ lfnName[i] = '\0';
+ }
+
+ return (true);
+}
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void)
+{
+ int i;
+
+ // Close all open files
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ if (openFiles[i].inUse == true)
+ {
+ FAT_fclose(&openFiles[i]);
+ }
+ }
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ // Clear card status
+ disc_ClearStatus();
+
+ // Return status of card
+ return disc_IsInserted();
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetDirEntry
+Return the file info structure of the next valid file entry
+u32 dirCluster: IN cluster of subdirectory table
+int entry: IN the desired file entry
+int origin IN: relative position of the entry
+DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if
+ the entry does not exist.
+-----------------------------------------------------------------*/
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin)
+{
+ DIR_ENT dir;
+ DIR_ENT_LFN lfn;
+ int firstSector = 0;
+ bool notFound = false;
+ bool found = false;
+ int maxSectors;
+ int lfnPos, aliasPos;
+ u8 lfnChkSum, chkSum;
+
+ int i;
+
+ dir.name[0] = FILE_FREE; // default to no file found
+ dir.attrib = 0x00;
+
+ // Check if fat has been initialised
+ if (filesysBytePerSec == 0)
+ {
+ return (dir);
+ }
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ break;
+ case SEEK_CUR: // Don't change anything
+ break;
+ case SEEK_END: // Find entry signifying end of directory
+ // Subtraction will never reach 0, so it keeps going
+ // until reaches end of directory
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ entry = -1;
+ break;
+ default:
+ return dir;
+ }
+
+ lfnChkSum = 0;
+ maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+
+ // Scan Dir for correct entry
+ firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster));
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ found = false;
+ notFound = false;
+ do {
+ wrkDirOffset++;
+ if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ wrkDirOffset = 0;
+ wrkDirSector++;
+ if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ wrkDirSector = 0;
+ wrkDirCluster = FAT_NextCluster(wrkDirCluster);
+ if (wrkDirCluster == CLUSTER_EOF)
+ {
+ notFound = true;
+ }
+ firstSector = FAT_ClustToSect(wrkDirCluster);
+ }
+ else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir)))
+ {
+ notFound = true; // Got to end of root dir
+ }
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ }
+ dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
+ if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL))
+ {
+ entry--;
+ if (lfnExists)
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 11; aliasPos++)
+ {
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]);
+ }
+ if (chkSum != lfnChkSum)
+ {
+ lfnExists = false;
+ lfnName[0] = '\0';
+ }
+ }
+ if (entry == 0)
+ {
+ if (!lfnExists)
+ {
+ FAT_GetFilename (dir, lfnName);
+ }
+ found = true;
+ }
+ }
+ else if (dir.name[0] == FILE_LAST)
+ {
+ if (origin == SEEK_END)
+ {
+ found = true;
+ }
+ else
+ {
+ notFound = true;
+ }
+ }
+ else if (dir.attrib == ATTRIB_LFN)
+ {
+ lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset];
+ if (lfn.ordinal & LFN_DEL)
+ {
+ lfnExists = false;
+ }
+ else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight)
+ {
+ lfnExists = true;
+ lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
+ lfnChkSum = lfn.checkSum;
+ }
+ if (lfnChkSum != lfn.checkSum)
+ {
+ lfnExists = false;
+ }
+ if (lfnExists)
+ {
+ lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13;
+ for (i = 0; i < 13; i++) {
+ lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/;
+ }
+ }
+ }
+ } while (!found && !notFound);
+
+ // If no file is found, return FILE_FREE
+ if (notFound)
+ {
+ dir.name[0] = FILE_FREE;
+ }
+
+ return (dir);
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile.
+ If a long name doesn't exist, it returns the short name
+ instead.
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename)
+{
+ if (filename == NULL)
+ return false;
+
+ strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1);
+ filename[MAX_FILENAME_LENGTH - 1] = '\0';
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetFilename
+Get the alias (short name) of the file or directory stored in
+ dirEntry
+DIR_ENT dirEntry: IN a valid directory table entry
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias)
+{
+ int i=0;
+ int j=0;
+
+ alias[0] = '\0';
+ if (dirEntry.name[0] != FILE_FREE)
+ {
+ if (dirEntry.name[0] == '.')
+ {
+ alias[0] = '.';
+ if (dirEntry.name[1] == '.')
+ {
+ alias[1] = '.';
+ alias[2] = '\0';
+ }
+ else
+ {
+ alias[1] = '\0';
+ }
+ }
+ else
+ {
+ // Copy the filename from the dirEntry to the string
+ for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++)
+ {
+ alias[i] = dirEntry.name[i];
+ }
+ // Copy the extension from the dirEntry to the string
+ if (dirEntry.ext[0] != ' ')
+ {
+ alias[i++] = '.';
+ for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++)
+ {
+ alias[i++] = dirEntry.ext[j];
+ }
+ }
+ alias[i] = '\0';
+ }
+ }
+
+ return (alias[0] != '\0');
+}
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias)
+{
+ if (alias == NULL)
+ {
+ return false;
+ }
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonLight
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask)
+{
+ // Get the file
+ if (!FAT_FileExists(filename)) {
+ return 0xff;
+ }
+
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes
+
+ disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate)
+{
+ struct tm timeInfo;
+
+ timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970
+ timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january
+ timeInfo.tm_mday = fileDate & 0x1f; // Day of the month
+
+ timeInfo.tm_hour = fileTime >> 11; // hours past midnight
+ timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour
+ timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute
+
+ return mktime(&timeInfo);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate);
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_DirEntFromPath
+Finds the directory entry for a file or directory from a path
+Path separator is a forward slash /
+const char* path: IN null terminated string of path.
+DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE
+ if the file was not found
+-----------------------------------------------------------------*/
+DIR_ENT FAT_DirEntFromPath (const char* path)
+{
+ int pathPos;
+ char name[MAX_FILENAME_LENGTH];
+ char alias[13];
+ int namePos;
+ bool found, notFound;
+ DIR_ENT dirEntry;
+ u32 dirCluster;
+ bool flagLFN, dotSeen;
+
+ // Start at beginning of path
+ pathPos = 0;
+
+ if (path[pathPos] == '/')
+ {
+ dirCluster = filesysRootDirClus; // Start at root directory
+ }
+ else
+ {
+ dirCluster = curWorkDirCluster; // Start at current working dir
+ }
+
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search until can't continue
+ found = false;
+ notFound = false;
+ while (!notFound && !found)
+ {
+ flagLFN = false;
+ // Copy name from path
+ namePos = 0;
+ if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) {
+ // Dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){
+ // Double dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ name[namePos++] = '.';
+ pathPos++;
+ } else {
+ // Copy name from path
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+ dotSeen = false;
+ while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/'))
+ {
+ name[namePos] = ucase(path[pathPos]);
+ if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (name[namePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ namePos++;
+ pathPos++;
+ }
+ // Check if a long filename was specified
+ if (namePos > 12)
+ {
+ flagLFN = true;
+ }
+ }
+
+ // Add end of string char
+ name[namePos] = '\0';
+
+ // Move through path to correct place
+ while ((path[pathPos] != '/') && (path[pathPos] != '\0'))
+ pathPos++;
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search current Dir for correct entry
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET);
+ while ( !found && !notFound)
+ {
+ // Match filename
+ found = true;
+ for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(lfnName[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (lfnName[namePos] == '\0'))
+ {
+ found = false;
+ }
+
+ // Check against alias as well.
+ if (!found)
+ {
+ FAT_GetFilename(dirEntry, alias);
+ found = true;
+ for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(alias[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (alias[namePos] == '\0'))
+ {
+ found = false;
+ }
+ }
+
+ if (dirEntry.name[0] == FILE_FREE)
+ // Couldn't find specified file
+ {
+ found = false;
+ notFound = true;
+ }
+ if (!found && !notFound)
+ {
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR);
+ }
+ }
+
+ if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0'))
+ // It has found a directory from within the path that needs to be followed
+ {
+ found = false;
+ dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ }
+ }
+
+ if (notFound)
+ {
+ dirEntry.name[0] = FILE_FREE;
+ dirEntry.attrib = 0x00;
+ }
+
+ return (dirEntry);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_AddDirEntry
+Creates a new dir entry for a file
+Path separator is a forward slash /
+const char* path: IN null terminated string of path to file.
+DIR_ENT newDirEntry IN: The directory entry to use.
+int file IN: The file being added (optional, use -1 if not used)
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry)
+{
+ char filename[MAX_FILENAME_LENGTH];
+ int filePos, pathPos, aliasPos;
+ char tempChar;
+ bool flagLFN, dotSeen;
+ char fileAlias[13] = {0};
+ int tailNum;
+
+ unsigned char chkSum = 0;
+
+ u32 oldWorkDirCluster;
+
+ DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer;
+ u32 dirCluster;
+ int secOffset;
+ int entryOffset;
+ int maxSectors;
+ u32 firstSector;
+
+ DIR_ENT_LFN lfnEntry;
+ int lfnPos = 0;
+
+ int dirEntryLength = 0;
+ int dirEntryRemain = 0;
+ u32 tempDirCluster;
+ int tempSecOffset;
+ int tempEntryOffset;
+ bool dirEndFlag = false;
+
+ int i;
+
+ // Store current working directory
+ oldWorkDirCluster = curWorkDirCluster;
+
+ // Find filename within path and change to correct directory
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+ flagLFN = false;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ filename[filePos] = '\0';
+ if (FAT_chdir(filename) == false)
+ {
+ curWorkDirCluster = oldWorkDirCluster;
+ return false; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ filename[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Skip over last slashes
+ while (path[pathPos] == '/')
+ pathPos++;
+
+ // Check if the filename has a leading "."
+ // If so, it is an LFN
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+
+ // Copy name from path
+ filePos = 0;
+ dotSeen = false;
+
+ while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0'))
+ {
+ filename[filePos] = path[pathPos];
+ if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (filename[filePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ filePos++;
+ pathPos++;
+ if ((filePos > 8) && !dotSeen) {
+ flagLFN = true;
+ }
+ }
+
+ if (filePos == 0) // No filename
+ {
+ return false;
+ }
+
+ // Check if a long filename was specified
+ if (filePos > 12)
+ {
+ flagLFN = true;
+ }
+
+ // Check if extension is > 3 characters long
+ if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) {
+ flagLFN = true;
+ }
+
+ lfnPos = (filePos - 1) / 13;
+
+ // Add end of string char
+ filename[filePos++] = '\0';
+ // Clear remaining chars
+ while (filePos < MAX_FILENAME_LENGTH)
+ filename[filePos++] = 0x01; // Set for LFN compatibility
+
+
+ if (flagLFN)
+ {
+ // Generate short filename - always a 2 digit number for tail
+ // Get first 5 chars of alias from LFN
+ aliasPos = 0;
+ filePos = 0;
+ if (filename[filePos] == '.') {
+ filePos++;
+ }
+ for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ // Pad Alias with underscores
+ while (aliasPos < 5)
+ fileAlias[aliasPos++] = '_';
+
+ fileAlias[5] = '~';
+ fileAlias[8] = '.';
+ fileAlias[9] = ' ';
+ fileAlias[10] = ' ';
+ fileAlias[11] = ' ';
+ if (strchr (filename, '.') != NULL) {
+ while(filename[filePos] != '\0')
+ {
+ filePos++;
+ if (filename[filePos] == '.')
+ {
+ pathPos = filePos;
+ }
+ }
+ filePos = pathPos + 1; //pathPos is used as a temporary variable
+ // Copy first 3 characters of extension
+ for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ } else {
+ aliasPos = 9;
+ }
+
+ // Pad Alias extension with spaces
+ while (aliasPos < 12)
+ fileAlias[aliasPos++] = ' ';
+
+ fileAlias[12] = '\0';
+
+
+ // Get a valid tail number
+ tailNum = 0;
+ do {
+ tailNum++;
+ fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit
+ fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit
+ } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100));
+
+ if (tailNum < 100) // Found an alias not being used
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 12; aliasPos++)
+ {
+ // Skip '.'
+ if (fileAlias[aliasPos] == '.')
+ aliasPos++;
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos];
+ }
+ }
+ else // Couldn't find a valid alias
+ {
+ return false;
+ }
+
+ dirEntryLength = lfnPos + 2;
+ }
+ else // Its not a long file name
+ {
+ // Just copy alias straight from filename
+ for (aliasPos = 0; aliasPos < 13; aliasPos++)
+ {
+ tempChar = ucase(filename[aliasPos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos] = tempChar;
+ }
+ fileAlias[12] = '\0';
+
+ lfnPos = -1;
+
+ dirEntryLength = 1;
+ }
+
+ // Change dirEntry name to match alias
+ for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++)
+ {
+ newDirEntry.name[aliasPos] = fileAlias[aliasPos];
+ }
+ while (aliasPos < 8)
+ {
+ newDirEntry.name[aliasPos++] = ' ';
+ }
+ aliasPos = 0;
+ while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0'))
+ aliasPos++;
+ filePos = 0;
+ while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0'))
+ {
+ tempChar = fileAlias[aliasPos++];
+ if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?')
+ newDirEntry.ext[filePos++] = tempChar;
+ }
+ while (filePos < 3)
+ {
+ newDirEntry.ext[filePos++] = ' ';
+ }
+
+ // Scan Dir for free entry
+ dirCluster = curWorkDirCluster;
+ secOffset = 0;
+ entryOffset = 0;
+ maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+ firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster));
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+
+ // Search for a large enough space to fit in new directory entry
+ while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0))
+ {
+
+ entryOffset++;
+
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ entryOffset = 0;
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) )
+ {
+ dirEntryRemain--;
+ } else {
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+ }
+ }
+
+ // Modifying the last directory is a special case - have to erase following entries
+ if (dirEntries[entryOffset].name[0] == FILE_LAST)
+ {
+ dirEndFlag = true;
+ }
+
+ // Recall last used entry
+ dirCluster = tempDirCluster;
+ secOffset = tempSecOffset;
+ entryOffset = tempEntryOffset;
+ dirEntryRemain = dirEntryLength;
+
+ // Re-read in first sector that will be written to
+ if (dirEndFlag && (entryOffset == 0)) {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ // Add new directory entry
+ while (dirEntryRemain > 0)
+ {
+ // Move to next entry, first pass advances from last used entry
+ entryOffset++;
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ // Write out the current sector if we need to
+ entryOffset = 0;
+ if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass
+ {
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+ }
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ if (dirEndFlag)
+ {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+ }
+
+ // Generate LFN entries
+ if (lfnPos >= 0)
+ {
+ lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0);
+ for (i = 0; i < 13; i++) {
+ if (filename [lfnPos * 13 + i] == 0x01) {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff;
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff;
+ } else {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i];
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00;
+ }
+ }
+
+ lfnEntry.checkSum = chkSum;
+ lfnEntry.flag = ATTRIB_LFN;
+ lfnEntry.reserved1 = 0;
+ lfnEntry.reserved2 = 0;
+
+ *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry;
+ lfnPos --;
+ lfnEntry.ordinal = 0;
+ } // end writing long filename entries
+ else
+ {
+ dirEntries[entryOffset] = newDirEntry;
+ if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) )
+ dirEntries[entryOffset+1].name[0] = FILE_LAST;
+ }
+
+ dirEntryRemain--;
+ }
+
+ // Write directory back to disk
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+
+ // Change back to Working DIR
+ curWorkDirCluster = oldWorkDirCluster;
+
+ return true;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile(char* filename)
+{
+ // Get the next directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile(char* filename)
+{
+ // Get the first directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindFirstFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindNextFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists(const char* filename)
+{
+ DIR_ENT dirEntry;
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (filename);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return FT_NONE;
+ }
+ else if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return FT_DIR;
+ }
+ else
+ {
+ return FT_FILE;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void)
+{
+ return filesysType;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void)
+{
+ return filesysTotalSize;
+}
+
+
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path)
+{
+ DIR_ENT dir;
+ if (path[0] == '/' && path[1] == '\0')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ return true;
+ }
+ if (path[0] == '\0') // Return true if changing relative to nothing
+ {
+ return true;
+ }
+
+ dir = FAT_DirEntFromPath (path);
+
+ if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE))
+ {
+ // Change directory
+ curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16);
+
+ // Move to correct cluster for root directory
+ if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER)
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ // Reset file position in directory
+ wrkDirCluster = curWorkDirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ return true;
+ }
+ else
+ {
+ // Couldn't change directory - wrong path specified
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns NULL if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode)
+{
+ int fileNum;
+ FAT_FILE* file;
+ DIR_ENT dirEntry;
+#ifdef CAN_WRITE_TO_DISC
+ u32 startCluster;
+ int clusCount;
+#endif
+
+ char* pchTemp;
+ // Check that a valid mode was specified
+ pchTemp = strpbrk ( mode, "rRwWaA" );
+ if (pchTemp == NULL)
+ {
+ return NULL;
+ }
+ if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL)
+ {
+ return NULL;
+ }
+
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Check that it is not a directory
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return NULL;
+ }
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check that it is not a read only file being openned in a writing mode
+ if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO))
+ {
+ return NULL;
+ }
+#else
+ if ( (strpbrk(mode, "wWaA+") != NULL))
+ {
+ return NULL;
+ }
+#endif
+
+ // Find a free file buffer
+ for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++);
+
+ if (fileNum == MAX_FILES_OPEN) // No free files
+ {
+ return NULL;
+ }
+
+ file = &openFiles[fileNum];
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+
+ if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // File must exist
+ {
+ return NULL;
+ }
+
+ file->read = true;
+#ifdef CAN_WRITE_TO_DISC
+ file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+#else
+ file->write = false;
+#endif
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check if file is openned for random. If it is, and currently has no cluster, one must be
+ // assigned to it.
+ if (file->write && file->firstCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+#endif
+
+ file->length = dirEntry.fileSize;
+ file->curPos = 0;
+ file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ } // mode "r"
+
+#ifdef CAN_WRITE_TO_DISC
+ if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ }
+ else // Already a file entry
+ {
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+ }
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (dirEntry.name[0] == FILE_FREE) // No file
+ {
+ // Have to create a new entry
+ if(!FAT_AddDirEntry (path, dirEntry))
+ {
+ return NULL;
+ }
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // Already a file
+ {
+ // Just modify the old entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+ file->write = true;
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = 0; // Should always have 0 bytes if openning in "w" mode
+ file->curPos = 0;
+ file->curClus = startCluster;
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ // Empty file, so empty read buffer
+ memset (file->readBuffer, 0, BYTE_PER_READ);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ }
+
+ if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+
+ // The file has no data in it
+ dirEntry.fileSize = 0;
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ if(!FAT_AddDirEntry (path, dirEntry))
+ return NULL;
+
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // File already exists - reuse the old directory entry
+ {
+ startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ // If it currently has no cluster, one must be assigned to it.
+ if (startCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ } else {
+
+ // Follow cluster list until last one is found
+ clusCount = dirEntry.fileSize / filesysBytePerClus;
+ file->appClus = startCluster;
+ while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF))
+ {
+ file->appClus = FAT_NextCluster (file->appClus);
+ }
+ if (clusCount >= 0) // Check if ran out of clusters
+ {
+ // Set flag to allocate new cluster when needed
+ file->appSect = filesysSecPerClus;
+ file->appByte = 0;
+ }
+ }
+ }
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL );
+ file->write = false;
+ file->append = true;
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ;
+ file->appByte = dirEntry.fileSize % BYTE_PER_READ;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = dirEntry.fileSize;
+ file->curPos = dirEntry.fileSize;
+ file->curClus = file->appClus;
+ file->curSect = file->appSect;
+ file->curByte = file->appByte;
+
+ // Read into buffer
+ disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer);
+ file->inUse = true; // We're using this file now
+ return file;
+ }
+#endif
+
+ // Can only reach here if a bad mode was specified
+ return NULL;
+}
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file)
+{
+ // Clear memory used by file information
+ if ((file != NULL) && (file->inUse == true))
+ {
+#ifdef CAN_WRITE_TO_DISC
+ if (file->write || file->append)
+ {
+ // Write new length, time and date back to directory entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length;
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate();
+
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ }
+#endif
+ file->inUse = false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file)
+{
+ // Return the position as specified in the FAT_FILE structure
+ if ((file != NULL) && (file->inUse == true))
+ {
+ return file->curPos;
+ }
+ else
+ {
+ // Return -1 if no file was given
+ return -1;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+FAT_FILE* file: IN handle of an open file
+s32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin)
+{
+ u32 cluster, nextCluster;
+ int clusCount;
+ u32 position;
+ u32 curPos;
+
+ if ((file == NULL) || (file->inUse == false)) // invalid file
+ {
+ return -1;
+ }
+
+ // Can't seek in append only mode
+ if (!file->read && !file->write)
+ {
+ return -1;
+ }
+
+ curPos = file->curPos;
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ if (offset >= 0)
+ {
+ position = offset;
+ } else {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ break;
+ case SEEK_CUR:
+ if (offset >= 0)
+ {
+ position = curPos + offset;
+ }
+ else if ( (u32)(offset * -1) >= curPos )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = curPos - (u32)(offset * -1);
+ }
+ break;
+ case SEEK_END:
+ if (offset >= 0)
+ {
+ // Seeking to end of file
+ position = file->length; // Fixed thanks to MoonLight
+ }
+ else if ( (u32)(offset * -1) >= file->length )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = file->length - (u32)(offset * -1);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (position > file->length)
+ {
+ // Tried to go past end of file
+ position = file->length;
+ }
+
+ // Save position
+ file->curPos = position;
+
+
+ // Calculate where the correct cluster is
+ if (position > curPos)
+ {
+ clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ
+ cluster = file->curClus;
+ } else {
+ clusCount = position / filesysBytePerClus;
+ cluster = file->firstCluster;
+ }
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ;
+ file->curByte = position % BYTE_PER_READ;
+
+ // Follow cluster list until desired one is found
+ if (clusCount > 0) // Only look at next cluster if need to
+ {
+ nextCluster = FAT_NextCluster (cluster);
+ } else {
+ nextCluster = cluster;
+ }
+ while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF))
+ {
+ cluster = nextCluster;
+ nextCluster = FAT_NextCluster (cluster);
+ }
+ // Check if ran out of clusters, and the file is being written to
+ if ((clusCount >= 0) && (file->write || file->append))
+ {
+ // Set flag to allocate a new cluster
+ file->curSect = filesysSecPerClus;
+ file->curByte = 0;
+ }
+ file->curClus = cluster;
+
+ // Reload sector buffer for new position in file, if it is a different sector
+ if ((curPos ^ position) >= BYTE_PER_READ)
+ {
+ disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer);
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in size * count bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+ u32 tempNextCluster;
+
+ int tempVar;
+
+ char* data = (char*)buffer;
+
+ u32 length = size * count;
+ u32 remain;
+
+ bool flagNoError = true;
+
+ // Can't read non-existant files
+ if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL)
+ return 0;
+
+ // Can only read files openned for reading
+ if (!file->read)
+ return 0;
+
+ // Don't read past end of file
+ if (length + file->curPos > file->length)
+ length = file->length - file->curPos;
+
+ remain = length;
+
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(data, &(file->readBuffer[curByte]), tempVar);
+ remain -= tempVar;
+ data += tempVar;
+
+ curByte += tempVar;
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // align to cluster
+ // tempVar is number of sectors to read
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+
+ curSect += tempVar;
+ }
+
+ // Move onto next cluster
+ // It should get to here without reading anything if a cluster is due to be allocated
+ if (curSect >= filesysSecPerClus)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read in whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+
+ // Advance to next cluster
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer);
+ if (remain > 0)
+ {
+ memcpy(data, file->readBuffer, remain);
+ curByte += remain;
+ remain = 0;
+ }
+ }
+
+ // Length read is the wanted length minus the stuff not read
+ length = length - remain;
+
+ // Update file information
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ return length;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+
+ u32 tempNextCluster;
+ int tempVar;
+ u32 length = size * count;
+ u32 remain = length;
+ char* data = (char*)buffer;
+
+ char* writeBuffer;
+
+ bool flagNoError = true;
+ bool flagAppending = false;
+
+ if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL)
+ return 0;
+
+ if (file->write)
+ {
+ // Write at current read pointer
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Use read buffer as write buffer
+ writeBuffer = file->readBuffer;
+
+ // If it is writing past the current end of file, set appending flag
+ if (length + file->curPos > file->length)
+ {
+ flagAppending = true;
+ }
+ }
+ else if (file->append)
+ {
+ // Write at end of file
+ curByte = file->appByte;
+ curSect = file->appSect;
+ curClus = file->appClus;
+ flagAppending = true;
+
+ // Use global buffer as write buffer, don't touch read buffer
+ writeBuffer = (char*)globalBuffer;
+ disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer);
+ }
+ else
+ {
+ return 0;
+ }
+
+ // Move onto next cluster if needed
+ if (curSect >= filesysSecPerClus)
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ memset(writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ curClus = tempNextCluster;
+ disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(&(writeBuffer[curByte]), data, tempVar);
+ remain -= tempVar;
+ data += tempVar;
+ curByte += tempVar;
+
+ // Write buffer back to disk
+ disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer);
+
+ // Move onto next sector
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // Align to cluster
+ // tempVar is number of sectors to write
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0))
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ }
+
+ // Write whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+ if (remain > 0)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ break;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ } else {
+ // Allocate a new cluster when next writing the file
+ curSect = filesysSecPerClus;
+ }
+ }
+
+ // Write remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ if (flagAppending)
+ {
+ // Zero sector before using it
+ memset (writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ // Modify existing sector
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ if (remain > 0) {
+ memcpy(writeBuffer, data, remain);
+ curByte += remain;
+ remain = 0;
+ disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Amount read is the originally requested amount minus stuff remaining
+ length = length - remain;
+
+ // Update file information
+ if (file->write) // Writing also shifts the read pointer
+ {
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ if (file->length < file->curPos)
+ {
+ file->length = file->curPos;
+ }
+ }
+ else if (file->append) // Appending doesn't affect the read pointer
+ {
+ file->appByte = curByte;
+ file->appSect = curSect;
+ file->appClus = curClus;
+ file->length = file->length + length;
+ }
+
+ return length;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file)
+{
+ if ((file == NULL) || (file->inUse == false))
+ return true; // Return eof on invalid files
+
+ return (file->length == file->curPos);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path)
+{
+ DIR_ENT dirEntry;
+ u32 oldWorkDirCluster;
+ char checkFilename[13];
+ FILE_TYPE checkFiletype;
+
+ dirEntry = FAT_DirEntFromPath (path);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return -1;
+ }
+
+ // Only delete directories if the directory is entry
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ // Change to the directory temporarily
+ oldWorkDirCluster = curWorkDirCluster;
+ FAT_chdir(path);
+
+ // Search for files or directories, excluding the . and .. entries
+ checkFiletype = FAT_FindFirstFile (checkFilename);
+ while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE))
+ {
+ checkFiletype = FAT_FindNextFile (checkFilename);
+ }
+
+ // Change back to working directory
+ curWorkDirCluster = oldWorkDirCluster;
+
+ // Check that the directory is empty
+ if (checkFiletype != FT_NONE)
+ {
+ // Directory isn't empty
+ return -1;
+ }
+ }
+
+ // Refresh directory information
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+
+ // Remove Directory entry
+ disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE;
+ disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ return 0;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path)
+{
+ u32 newDirCluster;
+ u32 parentDirCluster;
+ DIR_ENT dirEntry;
+ DIR_ENT* entries = (DIR_ENT*)globalBuffer;
+ int i;
+
+ int pathPos, filePos;
+ char pathname[MAX_FILENAME_LENGTH];
+ u32 oldDirCluster;
+
+ if (FAT_FileExists(path) != FT_NONE)
+ {
+ return -1; // File or directory exists with that name
+ }
+
+ // Find filename within path and change to that directory
+ oldDirCluster = curWorkDirCluster;
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ pathname[filePos] = '\0';
+ if (FAT_chdir(pathname) == false)
+ {
+ curWorkDirCluster = oldDirCluster;
+ return -1; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ pathname[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Now grab the parent directory's cluster
+ parentDirCluster = curWorkDirCluster;
+ curWorkDirCluster = oldDirCluster;
+
+ // Get a new cluster for the file
+ newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE);
+
+ if (newDirCluster == CLUSTER_FREE)
+ {
+ return -1; // Couldn't get a new cluster for the directory
+ }
+ // Fill in directory entry's information
+ dirEntry.attrib = ATTRIB_DIR;
+ dirEntry.reserved = 0;
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (newDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF);
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (FAT_AddDirEntry (path, dirEntry) == false)
+ {
+ return -1; // Couldn't add the directory entry
+ }
+
+ // Create the new directory itself
+ memset(entries, FILE_LAST, BYTE_PER_READ);
+
+ // Create . directory entry
+ dirEntry.name[0] = '.';
+ // Fill name and extension with spaces
+ for (i = 1; i < 11; i++)
+ {
+ dirEntry.name[i] = ' ';
+ }
+
+ memcpy(entries, &dirEntry, sizeof(dirEntry));
+
+ // Create .. directory entry
+ dirEntry.name[1] = '.';
+ dirEntry.startCluster = (parentDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF);
+
+ memcpy(&entries[1], &dirEntry, sizeof(dirEntry));
+
+ // Write entry to disc
+ disc_WriteSector(FAT_ClustToSect(newDirCluster), entries);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ return 0;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file)
+{
+ char c;
+ return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file)
+{
+ return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file)
+{
+ u32 curPos;
+ u32 readLength;
+ char *returnChar;
+
+ // invalid filehandle
+ if (file == NULL)
+ {
+ return NULL ;
+ }
+
+ // end of file
+ if (FAT_feof(file)==true)
+ {
+ return NULL ;
+ }
+
+ // save current position
+ curPos = FAT_ftell(file);
+
+ // read the full buffer (max string chars is num-1 and one end of string \0
+ readLength = FAT_fread(tgtBuffer,1,num-1,file) ;
+
+ // mark least possible end of string
+ tgtBuffer[readLength] = '\0' ;
+
+ if (readLength==0) {
+ // return error
+ return NULL ;
+ }
+
+ // get position of first return '\r'
+ returnChar = strchr(tgtBuffer,'\r');
+
+ // if no return is found, search for a newline
+ if (returnChar == NULL)
+ {
+ returnChar = strchr(tgtBuffer,'\n');
+ }
+
+ // Mark the return, if existant, as end of line/string
+ if (returnChar!=NULL) {
+ *returnChar++ = 0 ;
+ if (*returnChar=='\n') { // catch newline too when jumping over the end
+ // return to location after \r\n (strlen+2)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ;
+ return tgtBuffer ;
+ } else {
+ // return to location after \r (strlen+1)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ;
+ return tgtBuffer ;
+ }
+ }
+
+ return tgtBuffer ;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file)
+{
+ u32 writtenBytes;
+ // save string except end of string '\0'
+ writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file);
+
+ // check if we had an error
+ if (writtenBytes != strlen(string))
+ {
+ // return EOF error
+ return EOF;
+ }
+
+ // return the charcount written
+ return writtenBytes ;
+}
+#endif
+
+
+
diff --git a/backends/platform/ds/arm9/source/fat/gba_nds_fat.h b/backends/platform/ds/arm9/source/fat/gba_nds_fat.h
new file mode 100644
index 0000000000..8c44fafafb
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/gba_nds_fat.h
@@ -0,0 +1,888 @@
+/*
+ gba_nds_fat.h
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+
+#ifndef _GBA_NDS_FAT_INCLUDED
+#define _GBA_NDS_FAT_INCLUDED
+
+//---------------------------------------------------------------
+// Customisable features
+
+// Maximum number of files open at once
+// Increase this to open more files, decrease to save memory
+#define MAX_FILES_OPEN 4
+
+// Allow file writing
+// Disable this to remove file writing support
+#define CAN_WRITE_TO_DISC
+
+// Allow file time functions
+// This adds ~ 14KB to the compiled size
+// Uncomment to enable
+// #define FILE_TIME_SUPPORT
+
+//---------------------------------------------------------------
+// Platform specific includes
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+ #include <time.h>
+#endif
+
+//---------------------------------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+//---------------------------------------------------------------
+
+//---------------------------------------------------------------
+// Important constants
+
+
+#define MAX_FILENAME_LENGTH 256 // Maximum LFN length. Don't change this one
+
+// File Constants
+#ifndef EOF
+#define EOF -1
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+// File attributes
+#define ATTRIB_ARCH 0x20 // Archive
+#define ATTRIB_DIR 0x10 // Directory
+#define ATTRIB_LFN 0x0F // Long file name
+#define ATTRIB_VOL 0x08 // Volume
+#define ATTRIB_SYS 0x04 // System
+#define ATTRIB_HID 0x02 // Hidden
+#define ATTRIB_RO 0x01 // Read only
+
+
+// Directory Constants
+typedef enum {FT_NONE, FT_FILE, FT_DIR} FILE_TYPE;
+
+// Filesystem type
+typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
+
+// Open file information structure
+typedef struct
+{
+ u32 firstCluster;
+ u32 length;
+ u32 curPos;
+ u32 curClus; // Current cluster to read from
+ int curSect; // Current sector within cluster
+ int curByte; // Current byte within sector
+ char readBuffer[512]; // Buffer used for unaligned reads
+ u32 appClus; // Cluster to append to
+ int appSect; // Sector within cluster for appending
+ int appByte; // Byte within sector for appending
+ bool read; // Can read from file
+ bool write; // Can write to file
+ bool append;// Can append to file
+ bool inUse; // This file is open
+ u32 dirEntSector; // The sector where the directory entry is stored
+ int dirEntOffset; // The offset within the directory sector
+} FAT_FILE;
+
+
+//-----------------------------------------------------------------
+// CF Card functions
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias);
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonShine
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask);
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists (const char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void);
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path);
+
+
+//-----------------------------------------------------------------
+// File functions
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns -1 if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+int file: IN handle of an open file
+u32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in length number of bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path);
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* handle IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) ;
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file);
+#endif
+
+//------------------------------------------------------------------
+#ifdef __cplusplus
+} // extern "C"
+#endif
+//------------------------------------------------------------------
+
+#endif // ifndef _GBA_NDS_FAT
+
+/*
+ gba_nds_fat.h
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+
+#ifndef _GBA_NDS_FAT_INCLUDED
+#define _GBA_NDS_FAT_INCLUDED
+
+//---------------------------------------------------------------
+// Customisable features
+
+// Maximum number of files open at once
+// Increase this to open more files, decrease to save memory
+#define MAX_FILES_OPEN 4
+
+// Allow file writing
+// Disable this to remove file writing support
+#define CAN_WRITE_TO_DISC
+
+// Allow file time functions
+// This adds ~ 14KB to the compiled size
+// Uncomment to enable
+// #define FILE_TIME_SUPPORT
+
+//---------------------------------------------------------------
+// Platform specific includes
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+ #include <time.h>
+#endif
+
+//---------------------------------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+//---------------------------------------------------------------
+
+//---------------------------------------------------------------
+// Important constants
+
+
+#define MAX_FILENAME_LENGTH 256 // Maximum LFN length. Don't change this one
+
+// File Constants
+#ifndef EOF
+#define EOF -1
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+// File attributes
+#define ATTRIB_ARCH 0x20 // Archive
+#define ATTRIB_DIR 0x10 // Directory
+#define ATTRIB_LFN 0x0F // Long file name
+#define ATTRIB_VOL 0x08 // Volume
+#define ATTRIB_SYS 0x04 // System
+#define ATTRIB_HID 0x02 // Hidden
+#define ATTRIB_RO 0x01 // Read only
+
+
+// Directory Constants
+typedef enum {FT_NONE, FT_FILE, FT_DIR} FILE_TYPE;
+
+// Filesystem type
+typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
+
+// Open file information structure
+typedef struct
+{
+ u32 firstCluster;
+ u32 length;
+ u32 curPos;
+ u32 curClus; // Current cluster to read from
+ int curSect; // Current sector within cluster
+ int curByte; // Current byte within sector
+ char readBuffer[512]; // Buffer used for unaligned reads
+ u32 appClus; // Cluster to append to
+ int appSect; // Sector within cluster for appending
+ int appByte; // Byte within sector for appending
+ bool read; // Can read from file
+ bool write; // Can write to file
+ bool append;// Can append to file
+ bool inUse; // This file is open
+ u32 dirEntSector; // The sector where the directory entry is stored
+ int dirEntOffset; // The offset within the directory sector
+} FAT_FILE;
+
+
+//-----------------------------------------------------------------
+// CF Card functions
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias);
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonShine
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask);
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists (const char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void);
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path);
+
+
+//-----------------------------------------------------------------
+// File functions
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns -1 if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+int file: IN handle of an open file
+u32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in length number of bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path);
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* handle IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) ;
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file);
+#endif
+
+//------------------------------------------------------------------
+#ifdef __cplusplus
+} // extern "C"
+#endif
+//------------------------------------------------------------------
+
+#endif // ifndef _GBA_NDS_FAT
+
diff --git a/backends/platform/ds/arm9/source/fat/io_efa2.c b/backends/platform/ds/arm9/source/fat/io_efa2.c
new file mode 100644
index 0000000000..f3aa65cfcb
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_efa2.c
@@ -0,0 +1,642 @@
+/*
+io_efa2.c by CyteX
+
+Based on io_mpfc.c by chishm (Michael Chisholm)
+
+Hardware Routines for reading the NAND flash located on
+EFA2 flash carts
+
+This software is completely free. No warranty is provided.
+If you use it, please give me credit and email me about your
+project at cytex <at> gmx <dot> de and do not forget to also
+drop chishm <at> hotmail <dot> com a line
+
+See gba_nds_fat.txt for help and license details.
+*/
+
+#include "io_efa2.h"
+
+#ifdef SUPPORT_EFA2
+
+//
+// EFA2 register addresses
+//
+
+// RTC registers
+#define REG_RTC_CLK *(vu16*)0x080000c4
+#define REG_RTC_EN *(vu16*)0x080000c8
+
+// "Magic" registers used for unlock/lock sequences
+#define REG_EFA2_MAGIC_A *(vu16*)0x09fe0000
+#define REG_EFA2_MAGIC_B *(vu16*)0x08000000
+#define REG_EFA2_MAGIC_C *(vu16*)0x08020000
+#define REG_EFA2_MAGIC_D *(vu16*)0x08040000
+#define REG_EFA2_MAGIC_E *(vu16*)0x09fc0000
+
+// NAND flash lock/unlock register
+#define REG_EFA2_NAND_LOCK *(vu16*)0x09c40000
+// NAND flash enable register
+#define REG_EFA2_NAND_EN *(vu16*)0x09400000
+// NAND flash command write register
+#define REG_EFA2_NAND_CMD *(vu8*)0x09ffffe2
+// NAND flash address/data write register
+#define REG_EFA2_NAND_WR *(vu8*)0x09ffffe0
+// NAND flash data read register
+#define REG_EFA2_NAND_RD *(vu8*)0x09ffc000
+
+// ID of Samsung K9K1G NAND flash chip
+#define EFA2_NAND_ID 0xEC79A5C0
+
+// first sector of udisk
+#define EFA2_UDSK_START 0x40
+
+//
+// EFA2 access functions
+//
+
+// deactivate RTC ports
+inline void efa2_rtc_deactivate(void) {
+ REG_RTC_EN = 0;
+}
+
+// unlock register access
+void efa2_reg_unlock(void) {
+ REG_EFA2_MAGIC_A = 0x0d200;
+ REG_EFA2_MAGIC_B = 0x01500;
+ REG_EFA2_MAGIC_C = 0x0d200;
+ REG_EFA2_MAGIC_D = 0x01500;
+}
+
+// finish/lock register access
+inline void efa2_reg_lock(void) {
+ REG_EFA2_MAGIC_E = 0x1500;
+}
+
+// global reset/init/enable/unlock ?
+void efa2_global_unlock(void) {
+ efa2_reg_unlock();
+ *(vu16*)0x09880000 = 0x08000;
+ efa2_reg_lock();
+}
+
+// global lock, stealth mode
+void efa2_global_lock(void) {
+ // quite sure there is such a sequence, but haven't had
+ // a look for it upto now
+}
+
+// unlock NAND Flash
+void efa2_nand_unlock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x01500;
+ efa2_reg_lock();
+}
+
+// lock NAND Flash
+void efa2_nand_lock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x0d200;
+ efa2_reg_lock();
+}
+
+//
+// Set NAND Flash chip enable and write protection bits ?
+//
+// val | ~CE | ~WP |
+// -----+-----+-----+
+// 0 | 0 | 0 |
+// 1 | 1 | 0 |
+// 3 | 1 | 1 |
+// -----+-----+-----+
+//
+void efa2_nand_enable(u16 val) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_EN = val;
+ efa2_reg_lock();
+}
+
+//
+// Perform NAND reset
+// NAND has to be unlocked and enabled when called
+//
+inline void efa2_nand_reset(void) {
+ REG_EFA2_NAND_CMD = 0xff; // write reset command
+}
+
+//
+// Read out NAND ID information, could be used for card detection
+//
+// | EFA2 1GBit |
+// ------------------+------------+
+// maker code | 0xEC |
+// device code | 0x79 |
+// don't care | 0xA5 |
+// multi plane code | 0xC0 |
+// ------------------+------------+
+//
+u32 efa2_nand_id(void) {
+ u8 byte;
+ u32 id;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+
+ REG_EFA2_NAND_CMD = 0x90; // write id command
+ REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle
+ byte = REG_EFA2_NAND_RD; // read maker code
+ id = byte;
+ byte = REG_EFA2_NAND_RD; // read device code
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read don't care
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read multi plane code
+ id = (id << 8) | byte;
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return (id);
+}
+
+//
+// Start of gba_nds_fat block device description
+//
+
+/*-----------------------------------------------------------------
+EFA2_ClearStatus
+Reads and checks NAND status information
+bool return OUT: true if NAND is idle
+-----------------------------------------------------------------*/
+bool EFA2_ClearStatus (void)
+{
+ // tbd: currently there is no write support, so always return
+ // true, there is no possibility for pending operations
+ return true;
+}
+
+/*-----------------------------------------------------------------
+EFA2_IsInserted
+Checks to see if the NAND chip used by the EFA2 is present
+bool return OUT: true if the correct NAND chip is found
+-----------------------------------------------------------------*/
+bool EFA2_IsInserted (void)
+{
+ EFA2_ClearStatus();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+EFA2_ReadSectors
+Read "numSecs" 512 byte sectors starting from "sector" into "buffer"
+No error correction, no use of spare cells, no use of R/~B signal
+u32 sector IN: number of first 512 byte sector to be read
+u8 numSecs IN: number of 512 byte sectors to read,
+1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+
+#ifndef _CF_ALLOW_UNALIGNED
+ u8 byte;
+ u16 word;
+#endif
+
+ // NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the
+ // udisk and thus is sector 0. The original EFA2 firmware
+ // does never look at this, it only watches page 0x60, which
+ // contains the boot block of the FAT16 partition. That is
+ // fixed, so the EFA2 udisk must not be reformated, else
+ // the ARK Octopus and also the original Firmware won't be
+ // able to access the udisk anymore and I have to write a
+ // recovery tool.
+ u32 page = EFA2_UDSK_START + sector;
+
+ // future enhancement: wait for possible write operations to
+ // be finisched
+ if (!EFA2_ClearStatus()) return false;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+ efa2_nand_reset();
+
+ // set NAND to READ1 operation mode and transfer page address
+ REG_EFA2_NAND_CMD = 0x00; // write READ1 command
+ REG_EFA2_NAND_WR = 0x00; // write address [7:0]
+ REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8]
+ REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16]
+ REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24]
+
+ // Due to a bug in EFA2 design there is need to waste some cycles
+ // "by hand" instead the possibility to check the R/~B port of
+ // the NAND flash via a register. The RTC deactivation is only
+ // there to make sure the loop won't be optimized by the compiler
+ for (i=0 ; i < 3 ; i++) efa2_rtc_deactivate();
+
+ while (j--)
+ {
+ // read page data
+#ifdef _CF_ALLOW_UNALIGNED
+ // slow byte access to RAM, but works in principle
+ for (i=0 ; i < 512 ; i++)
+ ((u8*)buffer)[i] = REG_EFA2_NAND_RD;
+#else
+ // a bit faster, but DMA is not possible
+ for (i=0 ; i < 256 ; i++) {
+ byte = REG_EFA2_NAND_RD; // read lo-byte
+ word = byte;
+ byte = REG_EFA2_NAND_RD; // read hi-byte
+ word = word | (byte << 8);
+ ((u16*)buffer)[i] = word;
+ }
+#endif
+ }
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+EFA2_WriteSectors
+Write "numSecs" 512 byte sectors starting at "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on card to write
+u8 numSecs IN: number of 512 byte sectors to write
+1 to 256 sectors can be written, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ // Upto now I focused on reading NAND, write operations
+ // will follow
+ return false;
+}
+
+/*-----------------------------------------------------------------
+EFA2_Shutdown
+unload the EFA2 interface
+-----------------------------------------------------------------*/
+bool EFA2_Shutdown(void)
+{
+ return EFA2_ClearStatus();
+}
+
+/*-----------------------------------------------------------------
+EFA2_StartUp
+initializes the EFA2 card, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool EFA2_StartUp(void)
+{
+ efa2_global_unlock();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_efa2 = {
+ DEVICE_TYPE_EFA2,
+ FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&EFA2_StartUp,
+ (FN_MEDIUM_ISINSERTED)&EFA2_IsInserted,
+ (FN_MEDIUM_READSECTORS)&EFA2_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&EFA2_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&EFA2_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&EFA2_Shutdown
+};
+
+/*-----------------------------------------------------------------
+EFA2_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE EFA2_GetInterface(void) {
+ return &io_efa2;
+}
+
+#endif // SUPPORT_EFA2
+/*
+io_efa2.c by CyteX
+
+Based on io_mpfc.c by chishm (Michael Chisholm)
+
+Hardware Routines for reading the NAND flash located on
+EFA2 flash carts
+
+This software is completely free. No warranty is provided.
+If you use it, please give me credit and email me about your
+project at cytex <at> gmx <dot> de and do not forget to also
+drop chishm <at> hotmail <dot> com a line
+
+See gba_nds_fat.txt for help and license details.
+*/
+
+#include "io_efa2.h"
+
+#ifdef SUPPORT_EFA2
+
+//
+// EFA2 register addresses
+//
+
+// RTC registers
+#define REG_RTC_CLK *(vu16*)0x080000c4
+#define REG_RTC_EN *(vu16*)0x080000c8
+
+// "Magic" registers used for unlock/lock sequences
+#define REG_EFA2_MAGIC_A *(vu16*)0x09fe0000
+#define REG_EFA2_MAGIC_B *(vu16*)0x08000000
+#define REG_EFA2_MAGIC_C *(vu16*)0x08020000
+#define REG_EFA2_MAGIC_D *(vu16*)0x08040000
+#define REG_EFA2_MAGIC_E *(vu16*)0x09fc0000
+
+// NAND flash lock/unlock register
+#define REG_EFA2_NAND_LOCK *(vu16*)0x09c40000
+// NAND flash enable register
+#define REG_EFA2_NAND_EN *(vu16*)0x09400000
+// NAND flash command write register
+#define REG_EFA2_NAND_CMD *(vu8*)0x09ffffe2
+// NAND flash address/data write register
+#define REG_EFA2_NAND_WR *(vu8*)0x09ffffe0
+// NAND flash data read register
+#define REG_EFA2_NAND_RD *(vu8*)0x09ffc000
+
+// ID of Samsung K9K1G NAND flash chip
+#define EFA2_NAND_ID 0xEC79A5C0
+
+// first sector of udisk
+#define EFA2_UDSK_START 0x40
+
+//
+// EFA2 access functions
+//
+
+// deactivate RTC ports
+inline void efa2_rtc_deactivate(void) {
+ REG_RTC_EN = 0;
+}
+
+// unlock register access
+void efa2_reg_unlock(void) {
+ REG_EFA2_MAGIC_A = 0x0d200;
+ REG_EFA2_MAGIC_B = 0x01500;
+ REG_EFA2_MAGIC_C = 0x0d200;
+ REG_EFA2_MAGIC_D = 0x01500;
+}
+
+// finish/lock register access
+inline void efa2_reg_lock(void) {
+ REG_EFA2_MAGIC_E = 0x1500;
+}
+
+// global reset/init/enable/unlock ?
+void efa2_global_unlock(void) {
+ efa2_reg_unlock();
+ *(vu16*)0x09880000 = 0x08000;
+ efa2_reg_lock();
+}
+
+// global lock, stealth mode
+void efa2_global_lock(void) {
+ // quite sure there is such a sequence, but haven't had
+ // a look for it upto now
+}
+
+// unlock NAND Flash
+void efa2_nand_unlock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x01500;
+ efa2_reg_lock();
+}
+
+// lock NAND Flash
+void efa2_nand_lock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x0d200;
+ efa2_reg_lock();
+}
+
+//
+// Set NAND Flash chip enable and write protection bits ?
+//
+// val | ~CE | ~WP |
+// -----+-----+-----+
+// 0 | 0 | 0 |
+// 1 | 1 | 0 |
+// 3 | 1 | 1 |
+// -----+-----+-----+
+//
+void efa2_nand_enable(u16 val) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_EN = val;
+ efa2_reg_lock();
+}
+
+//
+// Perform NAND reset
+// NAND has to be unlocked and enabled when called
+//
+inline void efa2_nand_reset(void) {
+ REG_EFA2_NAND_CMD = 0xff; // write reset command
+}
+
+//
+// Read out NAND ID information, could be used for card detection
+//
+// | EFA2 1GBit |
+// ------------------+------------+
+// maker code | 0xEC |
+// device code | 0x79 |
+// don't care | 0xA5 |
+// multi plane code | 0xC0 |
+// ------------------+------------+
+//
+u32 efa2_nand_id(void) {
+ u8 byte;
+ u32 id;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+
+ REG_EFA2_NAND_CMD = 0x90; // write id command
+ REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle
+ byte = REG_EFA2_NAND_RD; // read maker code
+ id = byte;
+ byte = REG_EFA2_NAND_RD; // read device code
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read don't care
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read multi plane code
+ id = (id << 8) | byte;
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return (id);
+}
+
+//
+// Start of gba_nds_fat block device description
+//
+
+/*-----------------------------------------------------------------
+EFA2_ClearStatus
+Reads and checks NAND status information
+bool return OUT: true if NAND is idle
+-----------------------------------------------------------------*/
+bool EFA2_ClearStatus (void)
+{
+ // tbd: currently there is no write support, so always return
+ // true, there is no possibility for pending operations
+ return true;
+}
+
+/*-----------------------------------------------------------------
+EFA2_IsInserted
+Checks to see if the NAND chip used by the EFA2 is present
+bool return OUT: true if the correct NAND chip is found
+-----------------------------------------------------------------*/
+bool EFA2_IsInserted (void)
+{
+ EFA2_ClearStatus();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+EFA2_ReadSectors
+Read "numSecs" 512 byte sectors starting from "sector" into "buffer"
+No error correction, no use of spare cells, no use of R/~B signal
+u32 sector IN: number of first 512 byte sector to be read
+u8 numSecs IN: number of 512 byte sectors to read,
+1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+
+#ifndef _CF_ALLOW_UNALIGNED
+ u8 byte;
+ u16 word;
+#endif
+
+ // NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the
+ // udisk and thus is sector 0. The original EFA2 firmware
+ // does never look at this, it only watches page 0x60, which
+ // contains the boot block of the FAT16 partition. That is
+ // fixed, so the EFA2 udisk must not be reformated, else
+ // the ARK Octopus and also the original Firmware won't be
+ // able to access the udisk anymore and I have to write a
+ // recovery tool.
+ u32 page = EFA2_UDSK_START + sector;
+
+ // future enhancement: wait for possible write operations to
+ // be finisched
+ if (!EFA2_ClearStatus()) return false;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+ efa2_nand_reset();
+
+ // set NAND to READ1 operation mode and transfer page address
+ REG_EFA2_NAND_CMD = 0x00; // write READ1 command
+ REG_EFA2_NAND_WR = 0x00; // write address [7:0]
+ REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8]
+ REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16]
+ REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24]
+
+ // Due to a bug in EFA2 design there is need to waste some cycles
+ // "by hand" instead the possibility to check the R/~B port of
+ // the NAND flash via a register. The RTC deactivation is only
+ // there to make sure the loop won't be optimized by the compiler
+ for (i=0 ; i < 3 ; i++) efa2_rtc_deactivate();
+
+ while (j--)
+ {
+ // read page data
+#ifdef _CF_ALLOW_UNALIGNED
+ // slow byte access to RAM, but works in principle
+ for (i=0 ; i < 512 ; i++)
+ ((u8*)buffer)[i] = REG_EFA2_NAND_RD;
+#else
+ // a bit faster, but DMA is not possible
+ for (i=0 ; i < 256 ; i++) {
+ byte = REG_EFA2_NAND_RD; // read lo-byte
+ word = byte;
+ byte = REG_EFA2_NAND_RD; // read hi-byte
+ word = word | (byte << 8);
+ ((u16*)buffer)[i] = word;
+ }
+#endif
+ }
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+EFA2_WriteSectors
+Write "numSecs" 512 byte sectors starting at "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on card to write
+u8 numSecs IN: number of 512 byte sectors to write
+1 to 256 sectors can be written, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ // Upto now I focused on reading NAND, write operations
+ // will follow
+ return false;
+}
+
+/*-----------------------------------------------------------------
+EFA2_Shutdown
+unload the EFA2 interface
+-----------------------------------------------------------------*/
+bool EFA2_Shutdown(void)
+{
+ return EFA2_ClearStatus();
+}
+
+/*-----------------------------------------------------------------
+EFA2_StartUp
+initializes the EFA2 card, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool EFA2_StartUp(void)
+{
+ efa2_global_unlock();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_efa2 = {
+ DEVICE_TYPE_EFA2,
+ FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&EFA2_StartUp,
+ (FN_MEDIUM_ISINSERTED)&EFA2_IsInserted,
+ (FN_MEDIUM_READSECTORS)&EFA2_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&EFA2_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&EFA2_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&EFA2_Shutdown
+};
+
+/*-----------------------------------------------------------------
+EFA2_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE EFA2_GetInterface(void) {
+ return &io_efa2;
+}
+
+#endif // SUPPORT_EFA2
diff --git a/backends/platform/ds/arm9/source/fat/io_efa2.h b/backends/platform/ds/arm9/source/fat/io_efa2.h
new file mode 100644
index 0000000000..27c4e9beb8
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_efa2.h
@@ -0,0 +1,56 @@
+/*
+ io_efa2.h by CyteX
+
+ Based on io_mpfc.h by chishm (Michael Chisholm)
+
+ Hardware Routines for reading the NAND flash located on
+ EFA2 flash carts
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at cytex <at> gmx <dot> de and do not forget to also
+ drop chishm <at> hotmail <dot> com a line
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_EFA2_H
+#define IO_EFA2_H
+
+// 'EFA2'
+#define DEVICE_TYPE_EFA2 0x32414645
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE EFA2_GetInterface(void);
+
+#endif // define IO_EFA2_H
+/*
+ io_efa2.h by CyteX
+
+ Based on io_mpfc.h by chishm (Michael Chisholm)
+
+ Hardware Routines for reading the NAND flash located on
+ EFA2 flash carts
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at cytex <at> gmx <dot> de and do not forget to also
+ drop chishm <at> hotmail <dot> com a line
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_EFA2_H
+#define IO_EFA2_H
+
+// 'EFA2'
+#define DEVICE_TYPE_EFA2 0x32414645
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE EFA2_GetInterface(void);
+
+#endif // define IO_EFA2_H
diff --git a/backends/platform/ds/arm9/source/fat/io_fcsr.c b/backends/platform/ds/arm9/source/fat/io_fcsr.c
new file mode 100644
index 0000000000..8ca311ac92
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_fcsr.c
@@ -0,0 +1,658 @@
+/*
+ io_fcsr.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for using a GBA Flash Cart and SRAM as a
+ block device.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ The file system must be 512 byte aligned, in cart address space.
+ SRAM is supported.
+*/
+
+
+#include "io_fcsr.h"
+
+#ifdef SUPPORT_FCSR
+#include <string.h>
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+#ifdef NDS
+ #define SRAM_START 0x0A000000
+#else
+ #define SRAM_START 0x0E000000
+#endif
+
+#define NO_SRAM 0xFFFFFFFF
+
+#define FCSR 0x52534346
+const char FCSR_LabelString[] = " Chishm FAT";
+
+u8* FCSR_FileSysPointer = 0;
+u8* FCSR_SramSectorPointer[4] = {0,0,0,0};
+u32 FCSR_SramSectorStart[4] = {0,0,0,0};
+u32 FCSR_SramSectorEnd[4] = {0,0,0,0};
+
+/*-----------------------------------------------------------------
+FCSR_IsInserted
+Is a GBA Flash Cart with a valid file system inserted?
+bool return OUT: true if a GBA FC card is inserted
+-----------------------------------------------------------------*/
+bool FCSR_IsInserted (void)
+{
+ bool flagFoundFileSys = false;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ return flagFoundFileSys;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ClearStatus
+Finish any pending operations
+bool return OUT: always true for GBA FC
+-----------------------------------------------------------------*/
+bool FCSR_ClearStatus (void)
+{
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int numSectors = (numSecs > 0 ? numSecs : 256);
+ int readLength = numSectors * BYTE_PER_READ;
+ u8* src;;
+ u8* dst;
+
+ // Find which region this read is in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ // Make sure read will be completely in SRAM range if it is partially there
+ if ( flagSramSector && ((sector + numSectors) > FCSR_SramSectorEnd[i]))
+ return false;
+
+ // Copy data to buffer
+ if (flagSramSector)
+ {
+ src = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ } else {
+ src = FCSR_FileSysPointer + sector * BYTE_PER_READ;
+ }
+ dst = (u8*)buffer;
+
+ if (flagSramSector)
+ {
+ while (readLength--)
+ {
+ *dst++ = *src++;
+ }
+ } else { // Reading from Cart ROM
+
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ #ifdef ARM9
+ DC_FlushRange( buffer, readLength);
+ #endif // ARM9
+ DMA3_SRC = (u32)src;
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS;
+ #else // ! NDS
+ DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE);
+ #endif // NDS
+#else // !_CF_USE_DMA
+ memcpy (buffer, src, readLength);
+#endif // _CF_USE_DMA
+
+ } // if (flagSramSector)
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int writeLength = (numSecs > 0 ? numSecs : 256) * BYTE_PER_READ;
+ u8* src = (u8*) buffer;
+ u8* dst;
+
+ // Find which region this sector belongs in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ if (!flagSramSector)
+ return false;
+
+ // Entire write must be within an SRAM region
+ if ((sector + (numSecs > 0 ? numSecs : 256)) > FCSR_SramSectorEnd[i])
+ return false;
+
+ // Copy data to SRAM
+ dst = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ while (writeLength--)
+ {
+ *dst++ = *src++;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_Shutdown
+unload the Flash Cart interface
+-----------------------------------------------------------------*/
+bool FCSR_Shutdown(void)
+{
+ int i;
+ if (FCSR_ClearStatus() == false)
+ return false;
+
+ FCSR_FileSysPointer = 0;
+
+ for (i=0; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = 0;
+ FCSR_SramSectorStart[i] = 0;
+ FCSR_SramSectorEnd[i] = 0;
+ }
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_StartUp
+initializes the Flash Cart interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool FCSR_StartUp(void)
+{
+ bool flagFoundFileSys = false;
+ int i;
+ int SramRegionSize[4];
+ u8* srcByte;
+ u8* destByte;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ if (!flagFoundFileSys)
+ return false;
+
+ // Flash cart file system pointer has been found
+ FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40);
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ FCSR_SramSectorStart[i] = fileSysPointer[i+4];
+ SramRegionSize[i] = fileSysPointer[i+8];
+ FCSR_SramSectorEnd[i] = FCSR_SramSectorStart[i] + SramRegionSize[i];
+ }
+
+ // Calculate SRAM region pointers
+ FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4);
+ for (i = 1; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTE_PER_READ);
+ }
+
+ // Initialise SRAM with overlay if it hasn't been done so
+ if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') )
+ {
+ *((u8*)SRAM_START) = 'F';
+ *((u8*)(SRAM_START+1)) = 'C';
+ *((u8*)(SRAM_START+2)) = 'S';
+ *((u8*)(SRAM_START+3)) = 'R';
+
+ for (i = 0; i < 4; i++)
+ {
+ srcByte = FCSR_FileSysPointer + (FCSR_SramSectorStart[i] * BYTE_PER_READ);
+ destByte = FCSR_SramSectorPointer[i];
+ while (srcByte < FCSR_FileSysPointer + (FCSR_SramSectorEnd[i] * BYTE_PER_READ) )
+ *destByte++ = *srcByte++;
+ }
+ }
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ if (SramRegionSize[i] == 0)
+ {
+ FCSR_SramSectorStart[i] = NO_SRAM;
+ FCSR_SramSectorEnd[i] = NO_SRAM;
+ }
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_fcsr = {
+ DEVICE_TYPE_FCSR, // 'FCSR'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&FCSR_StartUp,
+ (FN_MEDIUM_ISINSERTED)&FCSR_IsInserted,
+ (FN_MEDIUM_READSECTORS)&FCSR_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&FCSR_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&FCSR_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&FCSR_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+FCSR_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE FCSR_GetInterface(void) {
+ return &io_fcsr ;
+} ;
+
+#endif // SUPPORT_FCSR
+/*
+ io_fcsr.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for using a GBA Flash Cart and SRAM as a
+ block device.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ The file system must be 512 byte aligned, in cart address space.
+ SRAM is supported.
+*/
+
+
+#include "io_fcsr.h"
+
+#ifdef SUPPORT_FCSR
+#include <string.h>
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+#ifdef NDS
+ #define SRAM_START 0x0A000000
+#else
+ #define SRAM_START 0x0E000000
+#endif
+
+#define NO_SRAM 0xFFFFFFFF
+
+#define FCSR 0x52534346
+const char FCSR_LabelString[] = " Chishm FAT";
+
+u8* FCSR_FileSysPointer = 0;
+u8* FCSR_SramSectorPointer[4] = {0,0,0,0};
+u32 FCSR_SramSectorStart[4] = {0,0,0,0};
+u32 FCSR_SramSectorEnd[4] = {0,0,0,0};
+
+/*-----------------------------------------------------------------
+FCSR_IsInserted
+Is a GBA Flash Cart with a valid file system inserted?
+bool return OUT: true if a GBA FC card is inserted
+-----------------------------------------------------------------*/
+bool FCSR_IsInserted (void)
+{
+ bool flagFoundFileSys = false;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ return flagFoundFileSys;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ClearStatus
+Finish any pending operations
+bool return OUT: always true for GBA FC
+-----------------------------------------------------------------*/
+bool FCSR_ClearStatus (void)
+{
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int numSectors = (numSecs > 0 ? numSecs : 256);
+ int readLength = numSectors * BYTE_PER_READ;
+ u8* src;;
+ u8* dst;
+
+ // Find which region this read is in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ // Make sure read will be completely in SRAM range if it is partially there
+ if ( flagSramSector && ((sector + numSectors) > FCSR_SramSectorEnd[i]))
+ return false;
+
+ // Copy data to buffer
+ if (flagSramSector)
+ {
+ src = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ } else {
+ src = FCSR_FileSysPointer + sector * BYTE_PER_READ;
+ }
+ dst = (u8*)buffer;
+
+ if (flagSramSector)
+ {
+ while (readLength--)
+ {
+ *dst++ = *src++;
+ }
+ } else { // Reading from Cart ROM
+
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ #ifdef ARM9
+ DC_FlushRange( buffer, readLength);
+ #endif // ARM9
+ DMA3_SRC = (u32)src;
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS;
+ #else // ! NDS
+ DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE);
+ #endif // NDS
+#else // !_CF_USE_DMA
+ memcpy (buffer, src, readLength);
+#endif // _CF_USE_DMA
+
+ } // if (flagSramSector)
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int writeLength = (numSecs > 0 ? numSecs : 256) * BYTE_PER_READ;
+ u8* src = (u8*) buffer;
+ u8* dst;
+
+ // Find which region this sector belongs in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ if (!flagSramSector)
+ return false;
+
+ // Entire write must be within an SRAM region
+ if ((sector + (numSecs > 0 ? numSecs : 256)) > FCSR_SramSectorEnd[i])
+ return false;
+
+ // Copy data to SRAM
+ dst = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ while (writeLength--)
+ {
+ *dst++ = *src++;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_Shutdown
+unload the Flash Cart interface
+-----------------------------------------------------------------*/
+bool FCSR_Shutdown(void)
+{
+ int i;
+ if (FCSR_ClearStatus() == false)
+ return false;
+
+ FCSR_FileSysPointer = 0;
+
+ for (i=0; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = 0;
+ FCSR_SramSectorStart[i] = 0;
+ FCSR_SramSectorEnd[i] = 0;
+ }
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_StartUp
+initializes the Flash Cart interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool FCSR_StartUp(void)
+{
+ bool flagFoundFileSys = false;
+ int i;
+ int SramRegionSize[4];
+ u8* srcByte;
+ u8* destByte;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ if (!flagFoundFileSys)
+ return false;
+
+ // Flash cart file system pointer has been found
+ FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40);
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ FCSR_SramSectorStart[i] = fileSysPointer[i+4];
+ SramRegionSize[i] = fileSysPointer[i+8];
+ FCSR_SramSectorEnd[i] = FCSR_SramSectorStart[i] + SramRegionSize[i];
+ }
+
+ // Calculate SRAM region pointers
+ FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4);
+ for (i = 1; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTE_PER_READ);
+ }
+
+ // Initialise SRAM with overlay if it hasn't been done so
+ if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') )
+ {
+ *((u8*)SRAM_START) = 'F';
+ *((u8*)(SRAM_START+1)) = 'C';
+ *((u8*)(SRAM_START+2)) = 'S';
+ *((u8*)(SRAM_START+3)) = 'R';
+
+ for (i = 0; i < 4; i++)
+ {
+ srcByte = FCSR_FileSysPointer + (FCSR_SramSectorStart[i] * BYTE_PER_READ);
+ destByte = FCSR_SramSectorPointer[i];
+ while (srcByte < FCSR_FileSysPointer + (FCSR_SramSectorEnd[i] * BYTE_PER_READ) )
+ *destByte++ = *srcByte++;
+ }
+ }
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ if (SramRegionSize[i] == 0)
+ {
+ FCSR_SramSectorStart[i] = NO_SRAM;
+ FCSR_SramSectorEnd[i] = NO_SRAM;
+ }
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_fcsr = {
+ DEVICE_TYPE_FCSR, // 'FCSR'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&FCSR_StartUp,
+ (FN_MEDIUM_ISINSERTED)&FCSR_IsInserted,
+ (FN_MEDIUM_READSECTORS)&FCSR_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&FCSR_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&FCSR_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&FCSR_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+FCSR_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE FCSR_GetInterface(void) {
+ return &io_fcsr ;
+} ;
+
+#endif // SUPPORT_FCSR
diff --git a/backends/platform/ds/arm9/source/fat/io_fcsr.h b/backends/platform/ds/arm9/source/fat/io_fcsr.h
new file mode 100644
index 0000000000..2f87c1c8aa
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_fcsr.h
@@ -0,0 +1,48 @@
+/*
+ io_fcsr.h
+
+ Hardware Routines for using a GBA Flash Cart with SRAM
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_FCSR_H
+#define IO_FCSR_H
+
+// 'FCSR'
+#define DEVICE_TYPE_FCSR 0x52534346
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE FCSR_GetInterface(void) ;
+
+#endif // define IO_FCSR_H
+/*
+ io_fcsr.h
+
+ Hardware Routines for using a GBA Flash Cart with SRAM
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_FCSR_H
+#define IO_FCSR_H
+
+// 'FCSR'
+#define DEVICE_TYPE_FCSR 0x52534346
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE FCSR_GetInterface(void) ;
+
+#endif // define IO_FCSR_H
diff --git a/backends/platform/ds/arm9/source/fat/io_m3cf.c b/backends/platform/ds/arm9/source/fat/io_m3cf.c
new file mode 100644
index 0000000000..238be7e311
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3cf.c
@@ -0,0 +1,734 @@
+/*
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3cf.h"
+
+#ifdef SUPPORT_M3CF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define M3_REG_STS *(vu16*)(GAME_PAK + 0x000C0000) // Status of the CF Card / Device control
+#define M3_REG_CMD *(vu16*)(GAME_PAK + 0x008E0000) // Commands sent to control chip and status return
+#define M3_REG_ERR *(vu16*)(GAME_PAK + 0x00820000) // Errors / Features
+
+#define M3_REG_SEC *(vu16*)(GAME_PAK + 0x00840000) // Number of sector to transfer
+#define M3_REG_LBA1 *(vu16*)(GAME_PAK + 0x00860000) // 1st byte of sector address
+#define M3_REG_LBA2 *(vu16*)(GAME_PAK + 0x00880000) // 2nd byte of sector address
+#define M3_REG_LBA3 *(vu16*)(GAME_PAK + 0x008A0000) // 3rd byte of sector address
+#define M3_REG_LBA4 *(vu16*)(GAME_PAK + 0x008C0000) // last nibble of sector address | 0xE0
+
+#define M3_DATA (vu16*)(GAME_PAK + 0x00800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+M3CF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3CF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED;
+ return ((M3_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3CF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ M3_REG_SEC = numSecs;
+
+ // Set read sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ M3_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)M3_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *M3_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *M3_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *M3_DATA;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+M3CF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ M3_REG_SEC = numSecs;
+
+ // Set write sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ M3_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)M3_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *M3_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *M3_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *M3_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3_Unlock(void)
+{
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ tmp = M3_REG_LBA1;
+ M3_REG_LBA1 = (~tmp & 0xFF);
+ tmp = (~tmp & 0xFF);
+ // did it change?
+ return (M3_REG_LBA1 == tmp) ;
+}
+
+bool M3CF_Shutdown(void) {
+ return M3CF_ClearStatus() ;
+} ;
+
+bool M3CF_StartUp(void) {
+ return M3_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3cf = {
+ DEVICE_TYPE_M3CF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&M3CF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3CF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3CF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3CF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3CF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3CF_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3CF_GetInterface(void) {
+ return &io_m3cf ;
+} ;
+
+#endif // SUPPORT_M3CF
+/*
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3cf.h"
+
+#ifdef SUPPORT_M3CF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define M3_REG_STS *(vu16*)(GAME_PAK + 0x000C0000) // Status of the CF Card / Device control
+#define M3_REG_CMD *(vu16*)(GAME_PAK + 0x008E0000) // Commands sent to control chip and status return
+#define M3_REG_ERR *(vu16*)(GAME_PAK + 0x00820000) // Errors / Features
+
+#define M3_REG_SEC *(vu16*)(GAME_PAK + 0x00840000) // Number of sector to transfer
+#define M3_REG_LBA1 *(vu16*)(GAME_PAK + 0x00860000) // 1st byte of sector address
+#define M3_REG_LBA2 *(vu16*)(GAME_PAK + 0x00880000) // 2nd byte of sector address
+#define M3_REG_LBA3 *(vu16*)(GAME_PAK + 0x008A0000) // 3rd byte of sector address
+#define M3_REG_LBA4 *(vu16*)(GAME_PAK + 0x008C0000) // last nibble of sector address | 0xE0
+
+#define M3_DATA (vu16*)(GAME_PAK + 0x00800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+M3CF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3CF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED;
+ return ((M3_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3CF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ M3_REG_SEC = numSecs;
+
+ // Set read sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ M3_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)M3_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *M3_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *M3_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *M3_DATA;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+M3CF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ M3_REG_SEC = numSecs;
+
+ // Set write sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ M3_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)M3_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *M3_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *M3_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *M3_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3_Unlock(void)
+{
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ tmp = M3_REG_LBA1;
+ M3_REG_LBA1 = (~tmp & 0xFF);
+ tmp = (~tmp & 0xFF);
+ // did it change?
+ return (M3_REG_LBA1 == tmp) ;
+}
+
+bool M3CF_Shutdown(void) {
+ return M3CF_ClearStatus() ;
+} ;
+
+bool M3CF_StartUp(void) {
+ return M3_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3cf = {
+ DEVICE_TYPE_M3CF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&M3CF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3CF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3CF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3CF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3CF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3CF_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3CF_GetInterface(void) {
+ return &io_m3cf ;
+} ;
+
+#endif // SUPPORT_M3CF
diff --git a/backends/platform/ds/arm9/source/fat/io_m3cf.h b/backends/platform/ds/arm9/source/fat/io_m3cf.h
new file mode 100644
index 0000000000..bade53f511
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3cf.h
@@ -0,0 +1,50 @@
+/*
+ io_m3cf.h
+
+ Hardware Routines for reading a compact flash card
+ using the M3 CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3CF_H
+#define IO_M3CF_H
+
+// 'M3CF'
+#define DEVICE_TYPE_M3CF 0x4643334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3CF_GetInterface(void) ;
+
+#endif // define IO_M3CF_H
+/*
+ io_m3cf.h
+
+ Hardware Routines for reading a compact flash card
+ using the M3 CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3CF_H
+#define IO_M3CF_H
+
+// 'M3CF'
+#define DEVICE_TYPE_M3CF 0x4643334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3CF_GetInterface(void) ;
+
+#endif // define IO_M3CF_H
diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd.c b/backends/platform/ds/arm9/source/fat/io_m3sd.c
new file mode 100644
index 0000000000..897f65bd6e
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3sd.c
@@ -0,0 +1,750 @@
+/*
+ io_m3sd.c based on io_m3cf.c by SaTa.
+
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3sd.h"
+
+#ifdef SUPPORT_M3SD
+
+//SD dir control bit cmddir=bit0 clken=bit1
+//output
+#define SDDIR (*(volatile u16*)0x8800000)
+
+//SD send get control bit send=bit0 get=bit1
+//output
+#define SDCON (*(volatile u16*)0x9800000)
+
+//SD output data obyte[7:0]=AD[7:0]
+//output
+#define SDODA (*(volatile u16*)0x9000000)
+
+//SD input data AD[7:0]=ibyte[7:0]
+//input
+#define SDIDA (*(volatile u16*)0x9000000)
+
+//readsector data1
+#define SDIDA1 (*(volatile u16*)0x9200000)
+
+//readsector data2
+#define SDIDA2 (*(volatile u16*)0x9400000)
+
+//readsector data3
+#define SDIDA3 (*(volatile u16*)0x9600000)
+
+//SD stutas cmdneg=bit0 cmdpos=bit1 issend=bit2 isget=bit3
+//input
+#define SDSTA (*(volatile u16*)0x9800000)
+
+//#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+#define CARD_TIMEOUT (500*100) // M3SD timeout nomal:500
+
+//======================================================
+bool M3SD_read1sector(u32 sectorn,u32 TAddr)
+{
+ u32 i;
+ int w;
+
+ SDCON=0x8; // bit3:ƒRƒ}ƒ“ƒhƒ‚[ƒhH
+ SDIDA1=0x40+17; // ƒRƒ}ƒ“ƒh CMD17
+ SDIDA2=(sectorn>>7);// ƒZƒNƒ^H 9ƒrƒbƒg=ƒAƒhƒŒƒXH ‚P‚Uƒrƒbƒg
+ SDIDA3=(sectorn<<9);// ƒZƒNƒ^L 7ƒrƒbƒg=ƒAƒhƒŒƒXL ‚P‚Uƒrƒbƒg
+ SDDIR=0x29; // ƒRƒ}ƒ“ƒh‘—MH
+ i=0;
+
+ while ( ((SDSTA&0x01) != 0x01)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+ i=0;
+ SDDIR=0x49;
+ while ( ((SDSTA&0x40) != 0x40)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+
+ SDDIR=0x8;//cmd input clken=0 datadir input clock=0
+ SDCON=0x4;//send=0 get=0 en25=1 cmd1=0
+
+ w = SDDIR;
+ for(w=0;w<0x100;w++)
+ {
+ u16 d16;
+ u8 *d8=(u8 *)&d16;
+// *(u16*)(TAddr+w*2) = SDDIR; // 16bit
+ d16 = SDDIR; // 16bit
+ *(u8 *)(TAddr+w*2) =d8[0];
+ *(u8 *)(TAddr+w*2+1) =d8[1];
+
+ }
+ w = SDDIR;
+ w = SDDIR;
+
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+
+}
+//==================================================
+
+
+//======================================================
+void SD_crc16(u16* buff,u16 num,u16* crc16buff);
+void SD_data_write(u16 *buff,u16* crc16buff);
+
+u16 Hal4ATA_StatusByte;
+
+void Hal4ATA_GetStatus(void)
+{
+ Hal4ATA_StatusByte = SDSTA;
+}
+
+bool Hal4ATA_WaitOnBusy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte & 0x01) != 0x1)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+bool Hal4ATA_WaitOnBusyNDrdy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte&0x40) !=0x40)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+
+void SendCommand(u16 command, u32 sectorn)
+{
+ SDCON=0x8;
+ SDIDA1=0x40+command;
+ SDIDA2=(sectorn>>7);
+ SDIDA3=(sectorn<<9);
+
+ SDDIR=0x29;
+ Hal4ATA_WaitOnBusy();
+ SDDIR=0x09;
+}
+
+
+#define DMA3SAD *(u32*)0x040000D4
+#define DMA3DAD *(u32*)0x040000D8
+#define DMA3CNT *(u32*)0x040000DC
+
+void DMA3(u32 src, u32 dst, u32 cnt)
+{
+ DMA3SAD=src;
+ DMA3DAD=dst;
+ DMA3CNT=cnt;
+}
+
+
+
+void PassRespond(u32 num)
+{
+ u32 i,dmanum;
+
+ dmanum=(64+(num<<3))>>2;
+ SDDIR=0x8;
+ SDCON=0x4;
+ DMA3(0x8800000,(u32)&i,0x80400000+dmanum);
+}
+
+//bool M3SD_write1sector(u32 sectorn,u16 * p)
+bool M3SD_write1sector(u32 sectorn,u32 p)
+{
+ u16 crc[4];
+
+ SendCommand(24,sectorn);
+ PassRespond(6);
+
+ SDDIR=0x4;
+ SDCON=0x0;
+
+ SD_crc16((u16 *)p,512,crc);
+ SD_data_write((u16 *)p,crc);
+ return true;
+}
+//==================================================
+
+
+// GBAMP CF Addresses
+
+#define M3_REG_STS *(vu16*)(0x09800000) // Status of the CF Card / Device control
+
+#define M3_DATA (vu16*)(0x08800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED1 0x20
+#define CF_STS_INSERTED2 0x30
+
+/*-----------------------------------------------------------------
+M3SD_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3SD_IsInserted (void)
+{
+ int i;
+ u16 sta;
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED1;
+
+ for(i=0;i<CARD_TIMEOUT;i++)
+ {
+ sta=M3_REG_STS;
+ if((sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2))
+ {
+ return true;
+ //break;
+ }
+ }
+ return false;
+
+// return ( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) );
+// return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3SD_ClearStatus (void)
+{
+
+// int i=SDDIR;
+ int i;
+ u16 sta;
+
+ i = 0;
+ M3_REG_STS = CF_STS_INSERTED1;
+ while (i < CARD_TIMEOUT)
+ {
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )break;
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+
+ //void M3SD_read1sector(u32 sectorn,u32 TAddr)
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_read1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+}
+
+
+
+/*-----------------------------------------------------------------
+M3SD_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_write1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+// return false;
+
+
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3SD_Unlock(void)
+{
+
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ vu16 sta;
+ sta=M3_REG_STS;
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )return true;
+
+ return false;
+}
+
+bool M3SD_Shutdown(void) {
+ return M3SD_ClearStatus() ;
+} ;
+
+bool M3SD_StartUp(void) {
+ return M3SD_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3sd = {
+ DEVICE_TYPE_M3SD,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&M3SD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3SD_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3SD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3SD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3SD_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3SD_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3SD_GetInterface(void) {
+ return &io_m3sd ;
+} ;
+
+#endif // SUPPORT_M3CF
+/*
+ io_m3sd.c based on io_m3cf.c by SaTa.
+
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3sd.h"
+
+#ifdef SUPPORT_M3SD
+
+//SD dir control bit cmddir=bit0 clken=bit1
+//output
+#define SDDIR (*(volatile u16*)0x8800000)
+
+//SD send get control bit send=bit0 get=bit1
+//output
+#define SDCON (*(volatile u16*)0x9800000)
+
+//SD output data obyte[7:0]=AD[7:0]
+//output
+#define SDODA (*(volatile u16*)0x9000000)
+
+//SD input data AD[7:0]=ibyte[7:0]
+//input
+#define SDIDA (*(volatile u16*)0x9000000)
+
+//readsector data1
+#define SDIDA1 (*(volatile u16*)0x9200000)
+
+//readsector data2
+#define SDIDA2 (*(volatile u16*)0x9400000)
+
+//readsector data3
+#define SDIDA3 (*(volatile u16*)0x9600000)
+
+//SD stutas cmdneg=bit0 cmdpos=bit1 issend=bit2 isget=bit3
+//input
+#define SDSTA (*(volatile u16*)0x9800000)
+
+//#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+#define CARD_TIMEOUT (500*100) // M3SD timeout nomal:500
+
+//======================================================
+bool M3SD_read1sector(u32 sectorn,u32 TAddr)
+{
+ u32 i;
+ int w;
+
+ SDCON=0x8; // bit3:ƒRƒ}ƒ“ƒhƒ‚[ƒhH
+ SDIDA1=0x40+17; // ƒRƒ}ƒ“ƒh CMD17
+ SDIDA2=(sectorn>>7);// ƒZƒNƒ^H 9ƒrƒbƒg=ƒAƒhƒŒƒXH ‚P‚Uƒrƒbƒg
+ SDIDA3=(sectorn<<9);// ƒZƒNƒ^L 7ƒrƒbƒg=ƒAƒhƒŒƒXL ‚P‚Uƒrƒbƒg
+ SDDIR=0x29; // ƒRƒ}ƒ“ƒh‘—MH
+ i=0;
+
+ while ( ((SDSTA&0x01) != 0x01)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+ i=0;
+ SDDIR=0x49;
+ while ( ((SDSTA&0x40) != 0x40)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+
+ SDDIR=0x8;//cmd input clken=0 datadir input clock=0
+ SDCON=0x4;//send=0 get=0 en25=1 cmd1=0
+
+ w = SDDIR;
+ for(w=0;w<0x100;w++)
+ {
+ u16 d16;
+ u8 *d8=(u8 *)&d16;
+// *(u16*)(TAddr+w*2) = SDDIR; // 16bit
+ d16 = SDDIR; // 16bit
+ *(u8 *)(TAddr+w*2) =d8[0];
+ *(u8 *)(TAddr+w*2+1) =d8[1];
+
+ }
+ w = SDDIR;
+ w = SDDIR;
+
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+
+}
+//==================================================
+
+
+//======================================================
+void SD_crc16(u16* buff,u16 num,u16* crc16buff);
+void SD_data_write(u16 *buff,u16* crc16buff);
+
+u16 Hal4ATA_StatusByte;
+
+void Hal4ATA_GetStatus(void)
+{
+ Hal4ATA_StatusByte = SDSTA;
+}
+
+bool Hal4ATA_WaitOnBusy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte & 0x01) != 0x1)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+bool Hal4ATA_WaitOnBusyNDrdy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte&0x40) !=0x40)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+
+void SendCommand(u16 command, u32 sectorn)
+{
+ SDCON=0x8;
+ SDIDA1=0x40+command;
+ SDIDA2=(sectorn>>7);
+ SDIDA3=(sectorn<<9);
+
+ SDDIR=0x29;
+ Hal4ATA_WaitOnBusy();
+ SDDIR=0x09;
+}
+
+
+#define DMA3SAD *(u32*)0x040000D4
+#define DMA3DAD *(u32*)0x040000D8
+#define DMA3CNT *(u32*)0x040000DC
+
+void DMA3(u32 src, u32 dst, u32 cnt)
+{
+ DMA3SAD=src;
+ DMA3DAD=dst;
+ DMA3CNT=cnt;
+}
+
+
+
+void PassRespond(u32 num)
+{
+ u32 i,dmanum;
+
+ dmanum=(64+(num<<3))>>2;
+ SDDIR=0x8;
+ SDCON=0x4;
+ DMA3(0x8800000,(u32)&i,0x80400000+dmanum);
+}
+
+//bool M3SD_write1sector(u32 sectorn,u16 * p)
+bool M3SD_write1sector(u32 sectorn,u32 p)
+{
+ u16 crc[4];
+
+ SendCommand(24,sectorn);
+ PassRespond(6);
+
+ SDDIR=0x4;
+ SDCON=0x0;
+
+ SD_crc16((u16 *)p,512,crc);
+ SD_data_write((u16 *)p,crc);
+ return true;
+}
+//==================================================
+
+
+// GBAMP CF Addresses
+
+#define M3_REG_STS *(vu16*)(0x09800000) // Status of the CF Card / Device control
+
+#define M3_DATA (vu16*)(0x08800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED1 0x20
+#define CF_STS_INSERTED2 0x30
+
+/*-----------------------------------------------------------------
+M3SD_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3SD_IsInserted (void)
+{
+ int i;
+ u16 sta;
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED1;
+
+ for(i=0;i<CARD_TIMEOUT;i++)
+ {
+ sta=M3_REG_STS;
+ if((sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2))
+ {
+ return true;
+ //break;
+ }
+ }
+ return false;
+
+// return ( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) );
+// return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3SD_ClearStatus (void)
+{
+
+// int i=SDDIR;
+ int i;
+ u16 sta;
+
+ i = 0;
+ M3_REG_STS = CF_STS_INSERTED1;
+ while (i < CARD_TIMEOUT)
+ {
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )break;
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+
+ //void M3SD_read1sector(u32 sectorn,u32 TAddr)
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_read1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+}
+
+
+
+/*-----------------------------------------------------------------
+M3SD_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_write1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+// return false;
+
+
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3SD_Unlock(void)
+{
+
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ vu16 sta;
+ sta=M3_REG_STS;
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )return true;
+
+ return false;
+}
+
+bool M3SD_Shutdown(void) {
+ return M3SD_ClearStatus() ;
+} ;
+
+bool M3SD_StartUp(void) {
+ return M3SD_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3sd = {
+ DEVICE_TYPE_M3SD,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&M3SD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3SD_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3SD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3SD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3SD_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3SD_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3SD_GetInterface(void) {
+ return &io_m3sd ;
+} ;
+
+#endif // SUPPORT_M3CF
diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd.h b/backends/platform/ds/arm9/source/fat/io_m3sd.h
new file mode 100644
index 0000000000..bd6b2644cc
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3sd.h
@@ -0,0 +1,50 @@
+/*
+ io_m3sd.h by SaTa.
+
+ Hardware Routines for reading an SD card
+ using the M3 SD
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3SD_H
+#define IO_M3SD_H
+
+// 'M3SD'
+#define DEVICE_TYPE_M3SD 0x4453334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3SD_GetInterface(void) ;
+
+#endif // define IO_M3SD_H
+/*
+ io_m3sd.h by SaTa.
+
+ Hardware Routines for reading an SD card
+ using the M3 SD
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3SD_H
+#define IO_M3SD_H
+
+// 'M3SD'
+#define DEVICE_TYPE_M3SD 0x4453334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3SD_GetInterface(void) ;
+
+#endif // define IO_M3SD_H
diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s b/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s
new file mode 100644
index 0000000000..18bbee75ce
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s
@@ -0,0 +1,392 @@
+ .TEXT
+@ AREA M3SD, CODE, READONLY
+@ ENTRY
+@-----------------------------------
+@ EXPORT SD_crc16
+@ EXPORT SD_data_write
+
+@CSYNC EQU 0x9800000
+@SDDIR EQU 0x8800000
+@SDCON EQU 0x9800000
+@SDODA EQU 0x9000000
+.equ CSYNC,0x9800000
+.equ SDDIR,0x8800000
+.equ SDCON,0x9800000
+.equ SDODA,0x9000000
+
+ .ALIGN
+ .CODE 32
+
+clkout:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x4
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0xc
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+clkin:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x0
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0x8
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+wait_ready:
+ stmfd r13!,{r0-r2}
+ mov r2,#32
+ mov r1,#SDODA
+sd_write_loop2:
+ mov r0,#0xff @end bit
+ strh r0,[r1]
+ bl clkout
+ subs r2, r2, #1
+ bne sd_write_loop2
+
+sd_write_busy:
+ bl clkin
+ ldrh r0,[r1]
+ tst r0,#0x100
+ beq sd_write_busy
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+@------void SD_crc16(u16* buff,u16 num,u16* crc16buff)
+
+ .GLOBAL SD_crc16
+
+SD_crc16:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end-----------------------------------
+
+@-----------------viod SD_data_write(u16 *buff,u16* crc16buff)-------------------
+ .GLOBAL SD_data_write
+SD_data_write:
+ stmfd r13!,{r4-r5,r14}
+ mov r5,#512
+ mov r2,#SDODA
+sd_data_write_busy:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+ bl clkout
+
+sd_data_write_loop:
+ ldrh r4,[r0],#2
+ mov r3,r4,lsr#4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#12
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#8
+ strh r3,[r2]
+ bl clkout
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r5,#32
+sd_data_write_loop2:
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+ bl clkout
+ subs r5, r5, #1
+ bne sd_data_write_loop2
+
+sd_data_write_busy2:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy2
+
+ ldmfd r13!,{r4-r5,r15}
+@-----------------end-------------------
+
+ .TEXT
+@ AREA M3SD, CODE, READONLY
+@ ENTRY
+@-----------------------------------
+@ EXPORT SD_crc16
+@ EXPORT SD_data_write
+
+@CSYNC EQU 0x9800000
+@SDDIR EQU 0x8800000
+@SDCON EQU 0x9800000
+@SDODA EQU 0x9000000
+.equ CSYNC,0x9800000
+.equ SDDIR,0x8800000
+.equ SDCON,0x9800000
+.equ SDODA,0x9000000
+
+ .ALIGN
+ .CODE 32
+
+clkout:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x4
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0xc
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+clkin:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x0
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0x8
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+wait_ready:
+ stmfd r13!,{r0-r2}
+ mov r2,#32
+ mov r1,#SDODA
+sd_write_loop2:
+ mov r0,#0xff @end bit
+ strh r0,[r1]
+ bl clkout
+ subs r2, r2, #1
+ bne sd_write_loop2
+
+sd_write_busy:
+ bl clkin
+ ldrh r0,[r1]
+ tst r0,#0x100
+ beq sd_write_busy
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+@------void SD_crc16(u16* buff,u16 num,u16* crc16buff)
+
+ .GLOBAL SD_crc16
+
+SD_crc16:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end-----------------------------------
+
+@-----------------viod SD_data_write(u16 *buff,u16* crc16buff)-------------------
+ .GLOBAL SD_data_write
+SD_data_write:
+ stmfd r13!,{r4-r5,r14}
+ mov r5,#512
+ mov r2,#SDODA
+sd_data_write_busy:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+ bl clkout
+
+sd_data_write_loop:
+ ldrh r4,[r0],#2
+ mov r3,r4,lsr#4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#12
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#8
+ strh r3,[r2]
+ bl clkout
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r5,#32
+sd_data_write_loop2:
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+ bl clkout
+ subs r5, r5, #1
+ bne sd_data_write_loop2
+
+sd_data_write_busy2:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy2
+
+ ldmfd r13!,{r4-r5,r15}
+@-----------------end-------------------
+
diff --git a/backends/platform/ds/arm9/source/fat/io_mpcf.c b/backends/platform/ds/arm9/source/fat/io_mpcf.c
new file mode 100644
index 0000000000..c55eef73eb
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_mpcf.c
@@ -0,0 +1,714 @@
+/*
+ io_mpcf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_mpcf.h"
+
+#ifdef SUPPORT_MPCF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define MP_REG_STS *(vu16*)(GAME_PAK + 0x018C0000) // Status of the CF Card / Device control
+#define MP_REG_CMD *(vu16*)(GAME_PAK + 0x010E0000) // Commands sent to control chip and status return
+#define MP_REG_ERR *(vu16*)(GAME_PAK + 0x01020000) // Errors / Features
+
+#define MP_REG_SEC *(vu16*)(GAME_PAK + 0x01040000) // Number of sector to transfer
+#define MP_REG_LBA1 *(vu16*)(GAME_PAK + 0x01060000) // 1st byte of sector address
+#define MP_REG_LBA2 *(vu16*)(GAME_PAK + 0x01080000) // 2nd byte of sector address
+#define MP_REG_LBA3 *(vu16*)(GAME_PAK + 0x010A0000) // 3rd byte of sector address
+#define MP_REG_LBA4 *(vu16*)(GAME_PAK + 0x010C0000) // last nibble of sector address | 0xE0
+
+#define MP_DATA (vu16*)(GAME_PAK + 0x01000000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+MPCF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool MPCF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ MP_REG_STS = CF_STS_INSERTED;
+ return ((MP_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool MPCF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if (defined _CF_USE_DMA) && (defined NDS) && (defined ARM9)
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ MP_REG_SEC = numSecs;
+
+ // Set read sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ MP_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((MP_REG_STS & 0xff)!= CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)MP_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *MP_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *MP_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *MP_DATA;
+#endif
+ }
+#if (defined _CF_USE_DMA) && (defined NDS)
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+MPCF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ MP_REG_SEC = numSecs;
+
+ // Set write sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ MP_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((MP_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)MP_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *MP_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *MP_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *MP_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+MPCF_Shutdown
+unload the GBAMP CF interface
+-----------------------------------------------------------------*/
+bool MPCF_Shutdown(void)
+{
+ return MPCF_ClearStatus() ;
+}
+
+/*-----------------------------------------------------------------
+MPCF_StartUp
+initializes the CF interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool MPCF_StartUp(void)
+{
+ u8 temp = MP_REG_LBA1;
+ MP_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (MP_REG_LBA1 == temp) ;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_mpcf = {
+ DEVICE_TYPE_MPCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&MPCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&MPCF_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+MPCF_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE MPCF_GetInterface(void) {
+ return &io_mpcf ;
+} ;
+
+#endif // SUPPORT_MPCF
+/*
+ io_mpcf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_mpcf.h"
+
+#ifdef SUPPORT_MPCF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define MP_REG_STS *(vu16*)(GAME_PAK + 0x018C0000) // Status of the CF Card / Device control
+#define MP_REG_CMD *(vu16*)(GAME_PAK + 0x010E0000) // Commands sent to control chip and status return
+#define MP_REG_ERR *(vu16*)(GAME_PAK + 0x01020000) // Errors / Features
+
+#define MP_REG_SEC *(vu16*)(GAME_PAK + 0x01040000) // Number of sector to transfer
+#define MP_REG_LBA1 *(vu16*)(GAME_PAK + 0x01060000) // 1st byte of sector address
+#define MP_REG_LBA2 *(vu16*)(GAME_PAK + 0x01080000) // 2nd byte of sector address
+#define MP_REG_LBA3 *(vu16*)(GAME_PAK + 0x010A0000) // 3rd byte of sector address
+#define MP_REG_LBA4 *(vu16*)(GAME_PAK + 0x010C0000) // last nibble of sector address | 0xE0
+
+#define MP_DATA (vu16*)(GAME_PAK + 0x01000000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+MPCF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool MPCF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ MP_REG_STS = CF_STS_INSERTED;
+ return ((MP_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool MPCF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if (defined _CF_USE_DMA) && (defined NDS) && (defined ARM9)
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ MP_REG_SEC = numSecs;
+
+ // Set read sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ MP_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((MP_REG_STS & 0xff)!= CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)MP_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *MP_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *MP_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *MP_DATA;
+#endif
+ }
+#if (defined _CF_USE_DMA) && (defined NDS)
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+MPCF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ MP_REG_SEC = numSecs;
+
+ // Set write sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ MP_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((MP_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)MP_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *MP_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *MP_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *MP_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+MPCF_Shutdown
+unload the GBAMP CF interface
+-----------------------------------------------------------------*/
+bool MPCF_Shutdown(void)
+{
+ return MPCF_ClearStatus() ;
+}
+
+/*-----------------------------------------------------------------
+MPCF_StartUp
+initializes the CF interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool MPCF_StartUp(void)
+{
+ u8 temp = MP_REG_LBA1;
+ MP_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (MP_REG_LBA1 == temp) ;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_mpcf = {
+ DEVICE_TYPE_MPCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&MPCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&MPCF_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+MPCF_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE MPCF_GetInterface(void) {
+ return &io_mpcf ;
+} ;
+
+#endif // SUPPORT_MPCF
diff --git a/backends/platform/ds/arm9/source/fat/io_mpcf.h b/backends/platform/ds/arm9/source/fat/io_mpcf.h
new file mode 100644
index 0000000000..58cab41b4c
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_mpcf.h
@@ -0,0 +1,50 @@
+/*
+ io_mpcf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_MPCF_H
+#define IO_MPCF_H
+
+// 'MPCF'
+#define DEVICE_TYPE_MPCF 0x4643504D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE MPCF_GetInterface(void) ;
+
+#endif // define IO_MPCF_H
+/*
+ io_mpcf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_MPCF_H
+#define IO_MPCF_H
+
+// 'MPCF'
+#define DEVICE_TYPE_MPCF 0x4643504D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE MPCF_GetInterface(void) ;
+
+#endif // define IO_MPCF_H
diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.c b/backends/platform/ds/arm9/source/fat/io_nmmc.c
new file mode 100644
index 0000000000..2e6b99de24
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_nmmc.c
@@ -0,0 +1,716 @@
+/*
+ io_nmmc.c
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ Submit bug reports for this device to the NeoFlash forums
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ 2006-02-09 - www.neoflash.com:
+ * First stable release
+
+ 2006-02-13 - Chishm
+ * Added ReadMK2Config function
+ * Added read config test to init function so no unnecessary card commands are sent
+ * Changed data read and write functions to use multiple block commands
+*/
+
+#include "io_nmmc.h"
+
+#ifdef SUPPORT_NMMC
+
+#include <nds/card.h>
+
+int spi_freq = 3;
+
+#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5)
+#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0))
+//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1))
+//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4)
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_READ_BLOCK 17
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_WRITE_BLOCK 24
+#define MMC_STOP_TRANSMISSION 12
+#define MMC_SET_BLOCKLEN 16
+#define MMC_SET_BLOCK_COUNT 23
+#define MMC_SEND_CSD 9
+
+// SPI functions
+
+static inline void Neo_OpenSPI( u8 frequency )
+{
+ CARD_CR1 = 0x0000A040 | frequency;
+}
+
+static inline u8 Neo_SPI( u8 dataByte )
+{
+ CARD_EEPDATA = dataByte;
+ while (CARD_CR1 & 0x80); // card busy
+ return CARD_EEPDATA;
+}
+
+static inline void Neo_CloseSPI ( void )
+{
+ CARD_CR1 = 0;
+}
+
+static inline void Neo_MK2GameMode() {
+ Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port
+ Neo_SPI(0xF1); // Switch to game mode
+ Neo_CloseSPI(); // Disable DS Card's SPI port
+}
+
+static inline void Neo_EnableEEPROM( bool enable ) {
+ Neo_OpenSPI(spi_freq);
+ if(enable) Neo_SPI(0x06);
+ else Neo_SPI(0x0E);
+ Neo_CloseSPI();
+}
+
+void Neo_WriteMK2Config(u8 config) {
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xFA); // Send mem conf write command
+ Neo_SPI(0x01); // Send high byte (0x01)
+ Neo_SPI(config); // Send low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+}
+
+u8 Neo_ReadMK2Config(void)
+{
+ u8 config;
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xf8); // Send mem conf read command
+ Neo_SPI(0x01); // Send high byte
+ config = Neo_SPI(0x00); // Get low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+ return config;
+}
+
+// Low level functions
+
+u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3};
+
+void Neo_SelectMMC (u8 dataByte)
+{
+ selectMMC_command[1] = dataByte; // Set enable / disable byte
+ cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card"
+ CARD_CR2 = CARD_ACTIVATE | CARD_nRESET;
+ while (CARD_CR2 & CARD_BUSY);
+ return;
+}
+
+void Neo_EnableMMC( bool enable )
+{
+ if ( enable == false) {
+ Neo_CloseSPI ();
+ Neo_SelectMMC (0);
+ Neo_SelectMMC (0);
+ } else {
+ Neo_SelectMMC (1);
+ Neo_SelectMMC (1);
+ Neo_OpenSPI (spi_freq);
+ }
+ return;
+}
+
+void Neo_SendMMCCommand( u8 command, u32 argument )
+{
+ Neo_SPI (0xFF);
+ Neo_SPI (command | 0x40);
+ Neo_SPI ((argument >> 24) & 0xff);
+ Neo_SPI ((argument >> 16) & 0xff);
+ Neo_SPI ((argument >> 8) & 0xff) ;
+ Neo_SPI (argument & 0xff);
+ Neo_SPI (0x95);
+ Neo_SPI (0xFF);
+ return;
+}
+
+bool Neo_CheckMMCResponse( u8 response, u8 mask ) {
+ u32 i;
+ for(i=0;i<256;i++) {
+ if( ( Neo_SPI( 0xFF ) & mask ) == response )
+ return true;
+ }
+ return false;
+}
+
+// Neo MMC functions
+
+bool Neo_InitMMC() {
+ Neo_MK2GameMode();
+ Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE);
+
+ // Make sure the configuration was accepted
+ if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) {
+ return false; // If not, then it wasn't initialised properly
+ }
+
+ return true;
+}
+
+// Neo MMC driver functions
+
+bool NMMC_IsInserted(void) {
+ int i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // consume data from card, and send clocks.
+ for (i = 0; i < 28; i++) {
+ Neo_SPI(0xff);
+ }
+
+ return true;
+}
+
+bool NMMC_ClearStatus (void) {
+ u32 i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ for (i = 0; i < 10; i++) {
+ Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card
+ }
+ Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command
+ if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error)
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for(i=0;i<256;i++) {
+ Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND
+ if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true; // Card is now idle
+ }
+ }
+ Neo_EnableMMC( false );
+ return false;
+}
+
+bool NMMC_Shutdown(void) {
+ return NMMC_ClearStatus();
+}
+
+bool NMMC_StartUp(void) {
+ int i;
+ int transSpeed;
+ if (Neo_InitMMC() == false) {
+ return false;
+ }
+ if (NMMC_ClearStatus() == false) {
+ return false;
+ }
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ // Set block length
+ Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // Check if we can use a higher SPI frequency
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for (i = 0; i < 3; i++) {
+ Neo_SPI(0xFF);
+ }
+ transSpeed = Neo_SPI (0xFF);
+ for (i = 0; i < 24; i++) {
+ Neo_SPI(0xFF);
+ }
+ if ((transSpeed & 0xf0) >= 0x30) {
+ spi_freq = 0;
+ }
+
+ Neo_EnableMMC( false );
+ return true;
+}
+
+
+bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand( 25, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ while (totalSecs--) {
+ Neo_SPI( 0xFC ); // Send Start Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data
+ Neo_SPI( *p++ );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted
+ Neo_EnableMMC( false );
+ return false;
+ }
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written
+ }
+
+ // Stop transmission block
+ Neo_SPI( 0xFD ); // Send Stop Transmission Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data
+ Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ Neo_SPI (0xFF); // Send 8 clocks
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear
+
+
+ for ( i = 0; i < 0x10; i++) {
+ Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ while (totalSecs--) {
+ Neo_SendMMCCommand(MMC_READ_BLOCK, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data
+ *p++ = Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ sector += BYTE_PER_READ;
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+
+IO_INTERFACE io_nmmc = {
+ DEVICE_TYPE_NMMC,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&NMMC_StartUp,
+ (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted,
+ (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown
+} ;
+
+
+LPIO_INTERFACE NMMC_GetInterface(void) {
+ return &io_nmmc ;
+}
+
+#endif // #ifdef SUPPORT_NMMC
+/*
+ io_nmmc.c
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ Submit bug reports for this device to the NeoFlash forums
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ 2006-02-09 - www.neoflash.com:
+ * First stable release
+
+ 2006-02-13 - Chishm
+ * Added ReadMK2Config function
+ * Added read config test to init function so no unnecessary card commands are sent
+ * Changed data read and write functions to use multiple block commands
+*/
+
+#include "io_nmmc.h"
+
+#ifdef SUPPORT_NMMC
+
+#include <nds/card.h>
+
+int spi_freq = 3;
+
+#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5)
+#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0))
+//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1))
+//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4)
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_READ_BLOCK 17
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_WRITE_BLOCK 24
+#define MMC_STOP_TRANSMISSION 12
+#define MMC_SET_BLOCKLEN 16
+#define MMC_SET_BLOCK_COUNT 23
+#define MMC_SEND_CSD 9
+
+// SPI functions
+
+static inline void Neo_OpenSPI( u8 frequency )
+{
+ CARD_CR1 = 0x0000A040 | frequency;
+}
+
+static inline u8 Neo_SPI( u8 dataByte )
+{
+ CARD_EEPDATA = dataByte;
+ while (CARD_CR1 & 0x80); // card busy
+ return CARD_EEPDATA;
+}
+
+static inline void Neo_CloseSPI ( void )
+{
+ CARD_CR1 = 0;
+}
+
+static inline void Neo_MK2GameMode() {
+ Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port
+ Neo_SPI(0xF1); // Switch to game mode
+ Neo_CloseSPI(); // Disable DS Card's SPI port
+}
+
+static inline void Neo_EnableEEPROM( bool enable ) {
+ Neo_OpenSPI(spi_freq);
+ if(enable) Neo_SPI(0x06);
+ else Neo_SPI(0x0E);
+ Neo_CloseSPI();
+}
+
+void Neo_WriteMK2Config(u8 config) {
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xFA); // Send mem conf write command
+ Neo_SPI(0x01); // Send high byte (0x01)
+ Neo_SPI(config); // Send low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+}
+
+u8 Neo_ReadMK2Config(void)
+{
+ u8 config;
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xf8); // Send mem conf read command
+ Neo_SPI(0x01); // Send high byte
+ config = Neo_SPI(0x00); // Get low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+ return config;
+}
+
+// Low level functions
+
+u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3};
+
+void Neo_SelectMMC (u8 dataByte)
+{
+ selectMMC_command[1] = dataByte; // Set enable / disable byte
+ cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card"
+ CARD_CR2 = CARD_ACTIVATE | CARD_nRESET;
+ while (CARD_CR2 & CARD_BUSY);
+ return;
+}
+
+void Neo_EnableMMC( bool enable )
+{
+ if ( enable == false) {
+ Neo_CloseSPI ();
+ Neo_SelectMMC (0);
+ Neo_SelectMMC (0);
+ } else {
+ Neo_SelectMMC (1);
+ Neo_SelectMMC (1);
+ Neo_OpenSPI (spi_freq);
+ }
+ return;
+}
+
+void Neo_SendMMCCommand( u8 command, u32 argument )
+{
+ Neo_SPI (0xFF);
+ Neo_SPI (command | 0x40);
+ Neo_SPI ((argument >> 24) & 0xff);
+ Neo_SPI ((argument >> 16) & 0xff);
+ Neo_SPI ((argument >> 8) & 0xff) ;
+ Neo_SPI (argument & 0xff);
+ Neo_SPI (0x95);
+ Neo_SPI (0xFF);
+ return;
+}
+
+bool Neo_CheckMMCResponse( u8 response, u8 mask ) {
+ u32 i;
+ for(i=0;i<256;i++) {
+ if( ( Neo_SPI( 0xFF ) & mask ) == response )
+ return true;
+ }
+ return false;
+}
+
+// Neo MMC functions
+
+bool Neo_InitMMC() {
+ Neo_MK2GameMode();
+ Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE);
+
+ // Make sure the configuration was accepted
+ if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) {
+ return false; // If not, then it wasn't initialised properly
+ }
+
+ return true;
+}
+
+// Neo MMC driver functions
+
+bool NMMC_IsInserted(void) {
+ int i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // consume data from card, and send clocks.
+ for (i = 0; i < 28; i++) {
+ Neo_SPI(0xff);
+ }
+
+ return true;
+}
+
+bool NMMC_ClearStatus (void) {
+ u32 i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ for (i = 0; i < 10; i++) {
+ Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card
+ }
+ Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command
+ if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error)
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for(i=0;i<256;i++) {
+ Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND
+ if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true; // Card is now idle
+ }
+ }
+ Neo_EnableMMC( false );
+ return false;
+}
+
+bool NMMC_Shutdown(void) {
+ return NMMC_ClearStatus();
+}
+
+bool NMMC_StartUp(void) {
+ int i;
+ int transSpeed;
+ if (Neo_InitMMC() == false) {
+ return false;
+ }
+ if (NMMC_ClearStatus() == false) {
+ return false;
+ }
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ // Set block length
+ Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // Check if we can use a higher SPI frequency
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for (i = 0; i < 3; i++) {
+ Neo_SPI(0xFF);
+ }
+ transSpeed = Neo_SPI (0xFF);
+ for (i = 0; i < 24; i++) {
+ Neo_SPI(0xFF);
+ }
+ if ((transSpeed & 0xf0) >= 0x30) {
+ spi_freq = 0;
+ }
+
+ Neo_EnableMMC( false );
+ return true;
+}
+
+
+bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand( 25, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ while (totalSecs--) {
+ Neo_SPI( 0xFC ); // Send Start Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data
+ Neo_SPI( *p++ );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted
+ Neo_EnableMMC( false );
+ return false;
+ }
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written
+ }
+
+ // Stop transmission block
+ Neo_SPI( 0xFD ); // Send Stop Transmission Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data
+ Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ Neo_SPI (0xFF); // Send 8 clocks
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear
+
+
+ for ( i = 0; i < 0x10; i++) {
+ Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ while (totalSecs--) {
+ Neo_SendMMCCommand(MMC_READ_BLOCK, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data
+ *p++ = Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ sector += BYTE_PER_READ;
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+
+IO_INTERFACE io_nmmc = {
+ DEVICE_TYPE_NMMC,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&NMMC_StartUp,
+ (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted,
+ (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown
+} ;
+
+
+LPIO_INTERFACE NMMC_GetInterface(void) {
+ return &io_nmmc ;
+}
+
+#endif // #ifdef SUPPORT_NMMC
diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.h b/backends/platform/ds/arm9/source/fat/io_nmmc.h
new file mode 100644
index 0000000000..5244d21868
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_nmmc.h
@@ -0,0 +1,54 @@
+/*
+ io_NMMC.h
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_NMMC_H
+#define IO_NMMC_H
+
+// 'NMMC'
+#define DEVICE_TYPE_NMMC 0x434D4D4E
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE NMMC_GetInterface(void) ;
+
+#endif // define IO_NMMC_H
+/*
+ io_NMMC.h
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_NMMC_H
+#define IO_NMMC_H
+
+// 'NMMC'
+#define DEVICE_TYPE_NMMC 0x434D4D4E
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE NMMC_GetInterface(void) ;
+
+#endif // define IO_NMMC_H
diff --git a/backends/platform/ds/arm9/source/fat/io_sccf.c b/backends/platform/ds/arm9/source/fat/io_sccf.c
new file mode 100644
index 0000000000..336387e9d7
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_sccf.c
@@ -0,0 +1,174 @@
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_sccf.h"
+
+#ifdef SUPPORT_SCCF
+
+#ifndef SUPPORT_MPCF
+ #error Supercard CF support requires GBAMP CF support
+#endif // SUPPORT_MPCF
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+
+/*-----------------------------------------------------------------
+SCCF_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCCF_Unlock(void)
+{
+#define CF_REG_LBA1 *(volatile unsigned short *)0x09060000
+ unsigned char temp;
+ volatile short *unlockAddress = (volatile short *)0x09FFFFFE;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0x3 ;
+ *unlockAddress = 0x3 ;
+ // provoke a ready reply
+ temp = CF_REG_LBA1;
+ CF_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (CF_REG_LBA1 == temp);
+#undef CF_REG_LBA1
+}
+
+bool SCCF_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCCF_StartUp(void) {
+ return SCCF_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_sccf = {
+ DEVICE_TYPE_SCCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&SCCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCCF_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCCF_GetInterface(void) {
+ return &io_sccf ;
+} ;
+
+#endif // SUPPORT_SCCF
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_sccf.h"
+
+#ifdef SUPPORT_SCCF
+
+#ifndef SUPPORT_MPCF
+ #error Supercard CF support requires GBAMP CF support
+#endif // SUPPORT_MPCF
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+
+/*-----------------------------------------------------------------
+SCCF_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCCF_Unlock(void)
+{
+#define CF_REG_LBA1 *(volatile unsigned short *)0x09060000
+ unsigned char temp;
+ volatile short *unlockAddress = (volatile short *)0x09FFFFFE;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0x3 ;
+ *unlockAddress = 0x3 ;
+ // provoke a ready reply
+ temp = CF_REG_LBA1;
+ CF_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (CF_REG_LBA1 == temp);
+#undef CF_REG_LBA1
+}
+
+bool SCCF_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCCF_StartUp(void) {
+ return SCCF_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_sccf = {
+ DEVICE_TYPE_SCCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&SCCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCCF_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCCF_GetInterface(void) {
+ return &io_sccf ;
+} ;
+
+#endif // SUPPORT_SCCF
diff --git a/backends/platform/ds/arm9/source/fat/io_sccf.h b/backends/platform/ds/arm9/source/fat/io_sccf.h
new file mode 100644
index 0000000000..961909fbce
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_sccf.h
@@ -0,0 +1,50 @@
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the Supercard CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCCF_H
+#define IO_SCCF_H
+
+// 'SCCF'
+#define DEVICE_TYPE_SCCF 0x46434353
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCCF_GetInterface(void) ;
+
+#endif // define IO_SCCF_H
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the Supercard CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCCF_H
+#define IO_SCCF_H
+
+// 'SCCF'
+#define DEVICE_TYPE_SCCF 0x46434353
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCCF_GetInterface(void) ;
+
+#endif // define IO_SCCF_H
diff --git a/backends/platform/ds/arm9/source/fat/io_scsd.c b/backends/platform/ds/arm9/source/fat/io_scsd.c
new file mode 100644
index 0000000000..a2fccf45d2
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_scsd.c
@@ -0,0 +1,186 @@
+/*
+ io_scsd.c by SaTa.
+ based on io_sccf.c
+
+
+*/
+
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_scsd.h"
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+// add by SaTa.
+extern void InitSCMode(void); // CF‚Æ“¯‚¶
+extern void ReadSector(u16 *buff,u32 sector,u8 ReadNumber);
+extern void WriteSector(u16 *buff,u32 sector,u8 writeNumber);
+extern bool MemoryCard_IsInserted(void); // CF‚ƈႤ
+//
+
+/*-----------------------------------------------------------------
+SCSD_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCSD_Unlock(void)
+{
+ InitSCMode();
+ return MemoryCard_IsInserted();
+}
+
+bool SCSD_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCSD_StartUp(void) {
+ return SCSD_Unlock() ;
+} ;
+
+bool SCSD_ReadSectors (u32 sector, u8 ReadNumber, void* buff)
+{
+ ReadSector((u16 *)buff,sector,ReadNumber);
+ return true;
+}
+
+bool SCSD_WriteSectors (u32 sector, u8 writeNumber, void* buff)
+{
+ WriteSector((u16 *)buff,sector,writeNumber);
+ return true;
+}
+
+
+IO_INTERFACE io_scsd = {
+ 0x44534353, // 'SCSD'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&SCSD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&SCSD_Unlock,
+ (FN_MEDIUM_READSECTORS)&SCSD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&SCSD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCSD_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCSD_GetInterface(void) {
+ return &io_scsd ;
+} ;
+/*
+ io_scsd.c by SaTa.
+ based on io_sccf.c
+
+
+*/
+
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_scsd.h"
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+// add by SaTa.
+extern void InitSCMode(void); // CF‚Æ“¯‚¶
+extern void ReadSector(u16 *buff,u32 sector,u8 ReadNumber);
+extern void WriteSector(u16 *buff,u32 sector,u8 writeNumber);
+extern bool MemoryCard_IsInserted(void); // CF‚ƈႤ
+//
+
+/*-----------------------------------------------------------------
+SCSD_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCSD_Unlock(void)
+{
+ InitSCMode();
+ return MemoryCard_IsInserted();
+}
+
+bool SCSD_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCSD_StartUp(void) {
+ return SCSD_Unlock() ;
+} ;
+
+bool SCSD_ReadSectors (u32 sector, u8 ReadNumber, void* buff)
+{
+ ReadSector((u16 *)buff,sector,ReadNumber);
+ return true;
+}
+
+bool SCSD_WriteSectors (u32 sector, u8 writeNumber, void* buff)
+{
+ WriteSector((u16 *)buff,sector,writeNumber);
+ return true;
+}
+
+
+IO_INTERFACE io_scsd = {
+ 0x44534353, // 'SCSD'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&SCSD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&SCSD_Unlock,
+ (FN_MEDIUM_READSECTORS)&SCSD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&SCSD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCSD_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCSD_GetInterface(void) {
+ return &io_scsd ;
+} ;
diff --git a/backends/platform/ds/arm9/source/fat/io_scsd.h b/backends/platform/ds/arm9/source/fat/io_scsd.h
new file mode 100644
index 0000000000..1e4e17dbb8
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_scsd.h
@@ -0,0 +1,58 @@
+/*
+ io_scsd.h by SaTa.
+ based on io_sccf.h
+
+
+*/
+
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCSD_H
+#define IO_SCSD_H
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCSD_GetInterface(void) ;
+
+#endif // define IO_SCSD_H
+/*
+ io_scsd.h by SaTa.
+ based on io_sccf.h
+
+
+*/
+
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCSD_H
+#define IO_SCSD_H
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCSD_GetInterface(void) ;
+
+#endif // define IO_SCSD_H
diff --git a/backends/platform/ds/arm9/source/fat/io_scsd_asm.s b/backends/platform/ds/arm9/source/fat/io_scsd_asm.s
new file mode 100644
index 0000000000..f138c07205
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_scsd_asm.s
@@ -0,0 +1,1042 @@
+ .TEXT
+@--------------------------------sd data--------------------------------
+.equ sd_comadd,0x9800000
+.equ sd_dataadd,0x9000000
+.equ sd_dataradd,0x9100000
+@-----------------viod sd_data_write_s(u16 *buff,u16* crc16buff)-------------------
+ .ALIGN
+ .GLOBAL sd_data_write_s
+ .CODE 32
+sd_data_write_s:
+ stmfd r13!,{r4-r5}
+ mov r5,#512
+ mov r2,#sd_dataadd
+sd_data_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ ldrh r3,[r2]
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+sd_data_write_loop:
+ ldrh r3,[r0],#2
+ add r3,r3,r3,lsl #20
+ mov r4,r3,lsl #8
+ stmia r2,{r3-r4}
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+sd_data_write_loop2:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ bne sd_data_write_loop2
+
+ ldmia r2,{r3-r4}
+
+ ldmfd r13!,{r4-r5}
+ bx r14
+@-----------------end sd_data_write_s-------------------
+
+@----------void sd_data_read_s(u16 *buff)-------------
+ .ALIGN
+ .GLOBAL sd_data_read_s
+ .CODE 32
+sd_data_read_s:
+ stmfd r13!,{r4}
+ mov r1,#sd_dataradd
+sd_data_read_loop1:
+ ldrh r3,[r1] @star bit
+ tst r3,#0x100
+ bne sd_data_read_loop1
+ mov r2,#512
+sd_data_read_loop:
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ subs r2, r2, #16
+ bne sd_data_read_loop
+
+ ldmia r1,{r3-r4} @crc 16
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldrh r3,[r1] @end bit
+ ldmfd r13!,{r4}
+ bx r14
+@----------end sd_data_read_s-------------
+
+@------void sd_com_crc16_s(u16* buff,u16 num,u16* crc16buff)
+ .ALIGN
+ .GLOBAL sd_crc16_s
+ .CODE 32
+sd_crc16_s:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end sd_com_crc16_s-----------------------------------
+
+@--------------u8 sd_crc7_s(u16* buff,u16 num)----------------------------
+ .ALIGN
+ .GLOBAL sd_crc7_s
+ .CODE 32
+sd_crc7_s:
+ stmfd r13!,{r4}
+
+ mov r3,#0
+ ldr r4,=0x80808080
+ mov r1,r1,lsl #3 @ *8
+sd_crc7_loop:
+ tst r4,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+
+ tst r3,#0x80
+ eorne r3,r3,#0x9
+
+ tst r2,r4,lsr #24
+ eorne r3,r3,#0x9
+
+ mov r4,r4,ror #1
+ subs r1,r1,#1
+ bne sd_crc7_loop
+
+ mov r3,r3,lsl #1
+ add r0,r3,#1
+ ldmfd r13!,{r4}
+ bx r14
+@--------------end sd_crc7_s----------------------------
+
+@--------------sd_com_read_s(u16* buff,u32 num)------------------
+ .ALIGN
+ .GLOBAL sd_com_read_s
+ .CODE 32
+
+sd_com_read_s:
+ stmfd r13!,{r4-r6}
+ mov r2,#sd_comadd
+sd_com_read_loop1:
+ ldrh r3,[r2] @star bit
+ tst r3,#1
+ bne sd_com_read_loop1
+
+sd_com_read_loop:
+ ldmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_read_loop
+ ldmfd r13!,{r4-r6}
+ bx r14
+@--------------end sd_com_read_s------------------
+
+@-------------------void sd_com_write_s(u16* buff,u32 num)-----------------
+
+ .ALIGN
+ .GLOBAL sd_com_write_s
+ .CODE 32
+sd_com_write_s:
+ stmfd r13!,{r4-r6}
+
+ mov r2,#sd_comadd
+sd_com_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x1
+ beq sd_com_write_busy
+
+ ldrh r3,[r2]
+
+sd_com_write_loop:
+ ldrb r3,[r0],#1
+ add r3,r3,r3,lsl #17
+ mov r4,r3,lsl #2
+ mov r5,r4,lsl #2
+ mov r6,r5,lsl #2
+ stmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_write_loop
+ ldmfd r13!,{r4-r6}
+
+ bx r14
+@-------------------end sd_com_write_s-----------------
+
+@-----------------void send_clk(u32 num)---------
+ .ALIGN
+ .GLOBAL send_clk
+ .CODE 32
+
+send_clk:
+ mov r1,#sd_comadd
+send_clk_loop1:
+ ldrh r3,[r1]
+ subs r0,r0,#1
+ bne send_clk_loop1
+ bx r14
+@-----------------end send_clk---------
+
+@---------------------------void SDCommand(u8 command,u8 num,u32 sector)--------------------
+ .ALIGN
+ .GLOBAL SDCommand
+ .CODE 32
+@void SDCommand(u8 command,u8 num,u32 sector )
+@{
+@ u8 databuff[6];
+@ register u8 *char1;
+@ register u8 *char2;
+@
+@ char1=(u8*)(((u32)&sector)+3);
+@ char2=(u8*)databuff;
+@ *char2++=coma+0x40;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1;
+@ *char2=sd_crc7_s((u32)databuff,5);
+@
+@ sd_com_write_s((u32)databuff,6);
+@
+@}
+SDCommand:
+ stmfd r13!,{r14}
+
+ sub r13,r13,#16
+ add r0,r0,#0x40
+ strb r0,[r13,#4]
+
+ mov r1,r2,lsr #24
+ strb r1,[r13,#5]
+ mov r1,r2,lsr #16
+ strb r1,[r13,#6]
+ mov r1,r2,lsr #8
+ strb r1,[r13,#7]
+ strb r2,[r13,#8]
+ add r0,r13,#4
+ mov r1,#5
+ bl sd_crc7_s
+ strb r0,[r13,#9]
+ add r0,r13,#4
+ mov r1,#6
+ bl sd_com_write_s
+
+ add r13,r13,#16
+ ldmfd r13!,{r15}
+@ bx r14
+@---------------------------end SDCommand--------------------
+
+@----------void ReadSector(u16 *buff,u32 sector,u8 readnum)-------------
+ .ALIGN
+ .GLOBAL ReadSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void ReadSector(u16 *buff,u32 sector,u8 readnum)
+@{
+@ register u16 i,j;
+@ i=readnum;
+@ sectno<<=9;
+@ SDCommand(18,0,sector);
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_data_read_s((u32)buff+j*512);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@
+@}
+ReadSector:
+ stmfd r13!,{r4-r6,r14}
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#18
+ mov r1,#0
+ bl SDCommand
+ mov r6,#0
+beginforj_ReadSector:
+ cmp r6,r5
+ bge endforj_ReadSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ bl sd_data_read_s
+ add r6,r6,#1
+ b beginforj_ReadSector
+endforj_ReadSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r0,#1
+ ldmfd r13!,{r4-r6,r15}
+@ bx r14
+@----------end ReadSector------------
+
+@-----------void get_resp(void)-------------------
+
+
+ .ALIGN
+ .GLOBAL get_resp @ r0:Srcp r1:num ok
+ .CODE 32
+get_resp:
+
+ stmfd r13!,{r14}
+ mov r1,#6
+ bl sd_com_read_s
+ ldmfd r13!,{r15}
+@-----------end get_resp-------------------
+
+
+@---------------void WriteSector(u16 *buff,u32 sector,u8 writenum)---------------------
+ .ALIGN
+ .GLOBAL WriteSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void WriteSector(u16 *buff,u32 sector,u8 writenum)
+@{
+@ register u16 i,j;
+@ u16 crc16[5];
+@ i=writenum;
+@
+@ sectno<<=9;
+@
+@ SDCommand(25,0,sector);
+@ get_resp();
+@ send_clk(0x10);
+@
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_crc16_s((u32)(u32)buff+j*512,512,(u32)crc16);
+@ sd_data_write_s((u32)buff+j*512,(u32)crc16);
+@ send_clk(0x10);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@ while((*(u16*)sd_dataadd &0x0100)==0);
+@
+@}
+@
+WriteSector:
+ stmfd r13!,{r4-r6,r14}
+
+ sub r13,r13,#20
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#25
+ mov r1,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r6,#0
+
+beginforj_WriteSector:
+ cmp r6,r5
+ bge endforj_WriteSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ mov r1,#512
+ add r2,r13,#4
+ bl sd_crc16_s
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ add r1,r13,#4
+ bl sd_data_write_s
+ mov r0,#0x10
+ bl send_clk
+ add r6,r6,#1
+ b beginforj_WriteSector
+endforj_WriteSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ ldr r0,=sd_dataadd
+beginwhile_WriteSector:
+ ldrh r1,[r0]
+ tst r1,#0x0100
+ beq beginwhile_WriteSector
+ mov r0,#1
+ add r13,r13,#20
+ ldmfd r13!,{r4-r6,r15}
+@---------------end WriteSector--------------------
+
+@----------------void InitSCMode(void)---------------
+ .ALIGN
+ .GLOBAL InitSCMode
+ .CODE 32
+InitSCMode:
+ mvn r0,#0x0F6000000
+ sub r0,r0,#0x01
+ mov r1,#0x0A500
+ add r1,r1,#0x5A
+ strh r1,[r0]
+ strh r1,[r0]
+ mov r2,#3
+ strh r2,[r0]
+ strh r2,[r0]
+ bx r14
+@----------------end InitSCMode ---------------
+
+@----------------bool MemoryCard_IsInserted(void)---------------
+ .ALIGN
+ .GLOBAL MemoryCard_IsInserted
+ .CODE 32
+
+MemoryCard_IsInserted:
+ ldr r0,=sd_comadd
+ ldrh r1,[r0]
+ tst r1,#0x300
+ moveq r0,#1
+ movne r0,#0
+ bx r14
+@----------------end MemoryCard_IsInserted---------------
+
+ .END
+
+
+
+
+
+
+
+
+
+
+
+ .TEXT
+@--------------------------------sd data--------------------------------
+.equ sd_comadd,0x9800000
+.equ sd_dataadd,0x9000000
+.equ sd_dataradd,0x9100000
+@-----------------viod sd_data_write_s(u16 *buff,u16* crc16buff)-------------------
+ .ALIGN
+ .GLOBAL sd_data_write_s
+ .CODE 32
+sd_data_write_s:
+ stmfd r13!,{r4-r5}
+ mov r5,#512
+ mov r2,#sd_dataadd
+sd_data_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ ldrh r3,[r2]
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+sd_data_write_loop:
+ ldrh r3,[r0],#2
+ add r3,r3,r3,lsl #20
+ mov r4,r3,lsl #8
+ stmia r2,{r3-r4}
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+sd_data_write_loop2:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ bne sd_data_write_loop2
+
+ ldmia r2,{r3-r4}
+
+ ldmfd r13!,{r4-r5}
+ bx r14
+@-----------------end sd_data_write_s-------------------
+
+@----------void sd_data_read_s(u16 *buff)-------------
+ .ALIGN
+ .GLOBAL sd_data_read_s
+ .CODE 32
+sd_data_read_s:
+ stmfd r13!,{r4}
+ mov r1,#sd_dataradd
+sd_data_read_loop1:
+ ldrh r3,[r1] @star bit
+ tst r3,#0x100
+ bne sd_data_read_loop1
+ mov r2,#512
+sd_data_read_loop:
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ subs r2, r2, #16
+ bne sd_data_read_loop
+
+ ldmia r1,{r3-r4} @crc 16
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldrh r3,[r1] @end bit
+ ldmfd r13!,{r4}
+ bx r14
+@----------end sd_data_read_s-------------
+
+@------void sd_com_crc16_s(u16* buff,u16 num,u16* crc16buff)
+ .ALIGN
+ .GLOBAL sd_crc16_s
+ .CODE 32
+sd_crc16_s:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end sd_com_crc16_s-----------------------------------
+
+@--------------u8 sd_crc7_s(u16* buff,u16 num)----------------------------
+ .ALIGN
+ .GLOBAL sd_crc7_s
+ .CODE 32
+sd_crc7_s:
+ stmfd r13!,{r4}
+
+ mov r3,#0
+ ldr r4,=0x80808080
+ mov r1,r1,lsl #3 @ *8
+sd_crc7_loop:
+ tst r4,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+
+ tst r3,#0x80
+ eorne r3,r3,#0x9
+
+ tst r2,r4,lsr #24
+ eorne r3,r3,#0x9
+
+ mov r4,r4,ror #1
+ subs r1,r1,#1
+ bne sd_crc7_loop
+
+ mov r3,r3,lsl #1
+ add r0,r3,#1
+ ldmfd r13!,{r4}
+ bx r14
+@--------------end sd_crc7_s----------------------------
+
+@--------------sd_com_read_s(u16* buff,u32 num)------------------
+ .ALIGN
+ .GLOBAL sd_com_read_s
+ .CODE 32
+
+sd_com_read_s:
+ stmfd r13!,{r4-r6}
+ mov r2,#sd_comadd
+sd_com_read_loop1:
+ ldrh r3,[r2] @star bit
+ tst r3,#1
+ bne sd_com_read_loop1
+
+sd_com_read_loop:
+ ldmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_read_loop
+ ldmfd r13!,{r4-r6}
+ bx r14
+@--------------end sd_com_read_s------------------
+
+@-------------------void sd_com_write_s(u16* buff,u32 num)-----------------
+
+ .ALIGN
+ .GLOBAL sd_com_write_s
+ .CODE 32
+sd_com_write_s:
+ stmfd r13!,{r4-r6}
+
+ mov r2,#sd_comadd
+sd_com_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x1
+ beq sd_com_write_busy
+
+ ldrh r3,[r2]
+
+sd_com_write_loop:
+ ldrb r3,[r0],#1
+ add r3,r3,r3,lsl #17
+ mov r4,r3,lsl #2
+ mov r5,r4,lsl #2
+ mov r6,r5,lsl #2
+ stmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_write_loop
+ ldmfd r13!,{r4-r6}
+
+ bx r14
+@-------------------end sd_com_write_s-----------------
+
+@-----------------void send_clk(u32 num)---------
+ .ALIGN
+ .GLOBAL send_clk
+ .CODE 32
+
+send_clk:
+ mov r1,#sd_comadd
+send_clk_loop1:
+ ldrh r3,[r1]
+ subs r0,r0,#1
+ bne send_clk_loop1
+ bx r14
+@-----------------end send_clk---------
+
+@---------------------------void SDCommand(u8 command,u8 num,u32 sector)--------------------
+ .ALIGN
+ .GLOBAL SDCommand
+ .CODE 32
+@void SDCommand(u8 command,u8 num,u32 sector )
+@{
+@ u8 databuff[6];
+@ register u8 *char1;
+@ register u8 *char2;
+@
+@ char1=(u8*)(((u32)&sector)+3);
+@ char2=(u8*)databuff;
+@ *char2++=coma+0x40;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1;
+@ *char2=sd_crc7_s((u32)databuff,5);
+@
+@ sd_com_write_s((u32)databuff,6);
+@
+@}
+SDCommand:
+ stmfd r13!,{r14}
+
+ sub r13,r13,#16
+ add r0,r0,#0x40
+ strb r0,[r13,#4]
+
+ mov r1,r2,lsr #24
+ strb r1,[r13,#5]
+ mov r1,r2,lsr #16
+ strb r1,[r13,#6]
+ mov r1,r2,lsr #8
+ strb r1,[r13,#7]
+ strb r2,[r13,#8]
+ add r0,r13,#4
+ mov r1,#5
+ bl sd_crc7_s
+ strb r0,[r13,#9]
+ add r0,r13,#4
+ mov r1,#6
+ bl sd_com_write_s
+
+ add r13,r13,#16
+ ldmfd r13!,{r15}
+@ bx r14
+@---------------------------end SDCommand--------------------
+
+@----------void ReadSector(u16 *buff,u32 sector,u8 readnum)-------------
+ .ALIGN
+ .GLOBAL ReadSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void ReadSector(u16 *buff,u32 sector,u8 readnum)
+@{
+@ register u16 i,j;
+@ i=readnum;
+@ sectno<<=9;
+@ SDCommand(18,0,sector);
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_data_read_s((u32)buff+j*512);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@
+@}
+ReadSector:
+ stmfd r13!,{r4-r6,r14}
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#18
+ mov r1,#0
+ bl SDCommand
+ mov r6,#0
+beginforj_ReadSector:
+ cmp r6,r5
+ bge endforj_ReadSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ bl sd_data_read_s
+ add r6,r6,#1
+ b beginforj_ReadSector
+endforj_ReadSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r0,#1
+ ldmfd r13!,{r4-r6,r15}
+@ bx r14
+@----------end ReadSector------------
+
+@-----------void get_resp(void)-------------------
+
+
+ .ALIGN
+ .GLOBAL get_resp @ r0:Srcp r1:num ok
+ .CODE 32
+get_resp:
+
+ stmfd r13!,{r14}
+ mov r1,#6
+ bl sd_com_read_s
+ ldmfd r13!,{r15}
+@-----------end get_resp-------------------
+
+
+@---------------void WriteSector(u16 *buff,u32 sector,u8 writenum)---------------------
+ .ALIGN
+ .GLOBAL WriteSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void WriteSector(u16 *buff,u32 sector,u8 writenum)
+@{
+@ register u16 i,j;
+@ u16 crc16[5];
+@ i=writenum;
+@
+@ sectno<<=9;
+@
+@ SDCommand(25,0,sector);
+@ get_resp();
+@ send_clk(0x10);
+@
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_crc16_s((u32)(u32)buff+j*512,512,(u32)crc16);
+@ sd_data_write_s((u32)buff+j*512,(u32)crc16);
+@ send_clk(0x10);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@ while((*(u16*)sd_dataadd &0x0100)==0);
+@
+@}
+@
+WriteSector:
+ stmfd r13!,{r4-r6,r14}
+
+ sub r13,r13,#20
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#25
+ mov r1,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r6,#0
+
+beginforj_WriteSector:
+ cmp r6,r5
+ bge endforj_WriteSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ mov r1,#512
+ add r2,r13,#4
+ bl sd_crc16_s
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ add r1,r13,#4
+ bl sd_data_write_s
+ mov r0,#0x10
+ bl send_clk
+ add r6,r6,#1
+ b beginforj_WriteSector
+endforj_WriteSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ ldr r0,=sd_dataadd
+beginwhile_WriteSector:
+ ldrh r1,[r0]
+ tst r1,#0x0100
+ beq beginwhile_WriteSector
+ mov r0,#1
+ add r13,r13,#20
+ ldmfd r13!,{r4-r6,r15}
+@---------------end WriteSector--------------------
+
+@----------------void InitSCMode(void)---------------
+ .ALIGN
+ .GLOBAL InitSCMode
+ .CODE 32
+InitSCMode:
+ mvn r0,#0x0F6000000
+ sub r0,r0,#0x01
+ mov r1,#0x0A500
+ add r1,r1,#0x5A
+ strh r1,[r0]
+ strh r1,[r0]
+ mov r2,#3
+ strh r2,[r0]
+ strh r2,[r0]
+ bx r14
+@----------------end InitSCMode ---------------
+
+@----------------bool MemoryCard_IsInserted(void)---------------
+ .ALIGN
+ .GLOBAL MemoryCard_IsInserted
+ .CODE 32
+
+MemoryCard_IsInserted:
+ ldr r0,=sd_comadd
+ ldrh r1,[r0]
+ tst r1,#0x300
+ moveq r0,#1
+ movne r0,#0
+ bx r14
+@----------------end MemoryCard_IsInserted---------------
+
+ .END
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backends/platform/ds/arm9/source/gbampsave.cpp b/backends/platform/ds/arm9/source/gbampsave.cpp
new file mode 100644
index 0000000000..bb5e44e722
--- /dev/null
+++ b/backends/platform/ds/arm9/source/gbampsave.cpp
@@ -0,0 +1,372 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "gbampsave.h"
+#include "gba_nds_fat.h"
+
+/////////////////////////
+// GBAMP Save File
+/////////////////////////
+
+GBAMPSaveFile::GBAMPSaveFile(char* name, bool saveOrLoad) {
+ handle = DS::std_fopen(name, saveOrLoad? "w": "r");
+ //consolePrintf("%s handle is %d\n", name, handle);
+// consolePrintf("Created %s\n", name);
+ bufferPos = 0;
+ saveSize = 0;
+ flushed = 0;
+}
+
+GBAMPSaveFile::~GBAMPSaveFile() {
+ flushSaveBuffer();
+ DS::std_fclose(handle);
+}
+
+uint32 GBAMPSaveFile::read(void *buf, uint32 size) {
+ saveSize += size;
+// consolePrintf("Read %d %d ", size, saveSize);
+ return DS::std_fread(buf, 1, size, handle);
+}
+
+bool GBAMPSaveFile::eos() const {
+ return DS::std_feof(handle);
+}
+
+void GBAMPSaveFile::skip(uint32 bytes) {
+ DS::std_fseek(handle, bytes, SEEK_CUR);
+}
+
+void GBAMPSaveFile::flushSaveBuffer() {
+ if (bufferPos != 0) {
+// consolePrintf("Flushing %d bytes\n", bufferPos);
+ flushed += bufferPos;
+ DS::std_fwrite(buffer, 1, bufferPos, handle);
+ bufferPos = 0;
+ }
+}
+
+uint32 GBAMPSaveFile::pos() const {
+ return DS::std_ftell(handle);
+}
+
+uint32 GBAMPSaveFile::size() const {
+ int position = pos();
+ DS::std_fseek(handle, 0, SEEK_END);
+ int size = DS::std_ftell(handle);
+ DS::std_fseek(handle, position, SEEK_SET);
+ return size;
+}
+
+void GBAMPSaveFile::seek(int32 pos, int whence) {
+ DS::std_fseek(handle, pos, whence);
+}
+
+
+uint32 GBAMPSaveFile::write(const void *buf, uint32 size) {
+ if (bufferPos + size > SAVE_BUFFER_SIZE) {
+ flushSaveBuffer();
+ saveSize += size;
+ DS::std_fwrite(buf, 1, size, handle);
+/* int pos = 0;
+
+ int rest = SAVE_BUFFER_SIZE - bufferPos;
+ memcpy(buffer + bufferPos, buf, rest);
+ bufferPos = 512;
+ pos += rest;
+ flushSaveBuffer();
+ size -= rest;
+// consolePrintf("First section: %d\n", rest);
+
+ while (size >= 512) {
+ DS::std_fwrite(((char *) (buf)) + pos, 1, 512, handle);
+ size -= 512;
+ pos += 512;
+// consolePrintf("Full chunk, %d left ", size);
+ }
+
+ bufferPos = 0;
+ memcpy(buffer + bufferPos, ((char *) (buf)) + pos, size);
+ bufferPos += size;
+// consolePrintf("%d left in buffer ", bufferPos);*/
+
+ } else {
+
+ memcpy(buffer + bufferPos, buf, size);
+ bufferPos += size;
+
+ saveSize += size;
+ }
+
+// if ((size > 100) || (size <= 0)) consolePrintf("Write %d bytes\n", size);
+ return size;
+}
+
+
+//////////////////////////
+// GBAMP Save File Manager
+//////////////////////////
+
+GBAMPSaveFileManager::GBAMPSaveFileManager() {
+
+}
+
+GBAMPSaveFileManager::~GBAMPSaveFileManager() {
+
+}
+
+Common::SaveFile* GBAMPSaveFileManager::openSavefile(char const* name, bool saveOrLoad) {
+ char fileSpec[128];
+
+ strcpy(fileSpec, getSavePath());
+
+ if (fileSpec[strlen(fileSpec) - 1] == '/') {
+ sprintf(fileSpec, "%s%s", getSavePath(), name);
+ } else {
+ sprintf(fileSpec, "%s/%s", getSavePath(), name);
+ }
+
+// consolePrintf(fileSpec);
+
+ return new GBAMPSaveFile(fileSpec, saveOrLoad);
+}
+
+void GBAMPSaveFileManager::listSavefiles(char const* prefix, bool* marks, int num) {
+ enum { TYPE_NO_MORE = 0, TYPE_FILE = 1, TYPE_DIR = 2 };
+ char name[128];
+ char path[128];
+
+ DS::std_cwd((char *) getSavePath());
+
+ int fileType = FAT_FindFirstFile(name);
+
+ for (int r = 0; r < num; r++) {
+ marks[r] = false;
+ }
+
+ do {
+
+ if (fileType == TYPE_FILE) {
+
+ FAT_GetLongFilename(name);
+
+ for (int r = 0; r < num; r++) {
+ char str[128];
+
+
+ sprintf(str, "%s%02d", prefix, r);
+// consolePrintf("%s != %s", str, name);
+ if (!stricmp(str, name)) {
+ marks[r] = true;
+// consolePrintf("Matched %d", r);
+ }
+
+ }
+
+ }
+
+ } while ((fileType = FAT_FindNextFile(name)));
+
+ FAT_chdir("/");
+}
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "gbampsave.h"
+#include "gba_nds_fat.h"
+
+/////////////////////////
+// GBAMP Save File
+/////////////////////////
+
+GBAMPSaveFile::GBAMPSaveFile(char* name, bool saveOrLoad) {
+ handle = DS::std_fopen(name, saveOrLoad? "w": "r");
+ //consolePrintf("%s handle is %d\n", name, handle);
+// consolePrintf("Created %s\n", name);
+ bufferPos = 0;
+ saveSize = 0;
+ flushed = 0;
+}
+
+GBAMPSaveFile::~GBAMPSaveFile() {
+ flushSaveBuffer();
+ DS::std_fclose(handle);
+}
+
+uint32 GBAMPSaveFile::read(void *buf, uint32 size) {
+ saveSize += size;
+// consolePrintf("Read %d %d ", size, saveSize);
+ return DS::std_fread(buf, 1, size, handle);
+}
+
+bool GBAMPSaveFile::eos() const {
+ return DS::std_feof(handle);
+}
+
+void GBAMPSaveFile::skip(uint32 bytes) {
+ DS::std_fseek(handle, bytes, SEEK_CUR);
+}
+
+void GBAMPSaveFile::flushSaveBuffer() {
+ if (bufferPos != 0) {
+// consolePrintf("Flushing %d bytes\n", bufferPos);
+ flushed += bufferPos;
+ DS::std_fwrite(buffer, 1, bufferPos, handle);
+ bufferPos = 0;
+ }
+}
+
+uint32 GBAMPSaveFile::pos() const {
+ return DS::std_ftell(handle);
+}
+
+uint32 GBAMPSaveFile::size() const {
+ int position = pos();
+ DS::std_fseek(handle, 0, SEEK_END);
+ int size = DS::std_ftell(handle);
+ DS::std_fseek(handle, position, SEEK_SET);
+ return size;
+}
+
+void GBAMPSaveFile::seek(int32 pos, int whence) {
+ DS::std_fseek(handle, pos, whence);
+}
+
+
+uint32 GBAMPSaveFile::write(const void *buf, uint32 size) {
+ if (bufferPos + size > SAVE_BUFFER_SIZE) {
+ flushSaveBuffer();
+ saveSize += size;
+ DS::std_fwrite(buf, 1, size, handle);
+/* int pos = 0;
+
+ int rest = SAVE_BUFFER_SIZE - bufferPos;
+ memcpy(buffer + bufferPos, buf, rest);
+ bufferPos = 512;
+ pos += rest;
+ flushSaveBuffer();
+ size -= rest;
+// consolePrintf("First section: %d\n", rest);
+
+ while (size >= 512) {
+ DS::std_fwrite(((char *) (buf)) + pos, 1, 512, handle);
+ size -= 512;
+ pos += 512;
+// consolePrintf("Full chunk, %d left ", size);
+ }
+
+ bufferPos = 0;
+ memcpy(buffer + bufferPos, ((char *) (buf)) + pos, size);
+ bufferPos += size;
+// consolePrintf("%d left in buffer ", bufferPos);*/
+
+ } else {
+
+ memcpy(buffer + bufferPos, buf, size);
+ bufferPos += size;
+
+ saveSize += size;
+ }
+
+// if ((size > 100) || (size <= 0)) consolePrintf("Write %d bytes\n", size);
+ return size;
+}
+
+
+//////////////////////////
+// GBAMP Save File Manager
+//////////////////////////
+
+GBAMPSaveFileManager::GBAMPSaveFileManager() {
+
+}
+
+GBAMPSaveFileManager::~GBAMPSaveFileManager() {
+
+}
+
+Common::SaveFile* GBAMPSaveFileManager::openSavefile(char const* name, bool saveOrLoad) {
+ char fileSpec[128];
+
+ strcpy(fileSpec, getSavePath());
+
+ if (fileSpec[strlen(fileSpec) - 1] == '/') {
+ sprintf(fileSpec, "%s%s", getSavePath(), name);
+ } else {
+ sprintf(fileSpec, "%s/%s", getSavePath(), name);
+ }
+
+// consolePrintf(fileSpec);
+
+ return new GBAMPSaveFile(fileSpec, saveOrLoad);
+}
+
+void GBAMPSaveFileManager::listSavefiles(char const* prefix, bool* marks, int num) {
+ enum { TYPE_NO_MORE = 0, TYPE_FILE = 1, TYPE_DIR = 2 };
+ char name[128];
+ char path[128];
+
+ DS::std_cwd((char *) getSavePath());
+
+ int fileType = FAT_FindFirstFile(name);
+
+ for (int r = 0; r < num; r++) {
+ marks[r] = false;
+ }
+
+ do {
+
+ if (fileType == TYPE_FILE) {
+
+ FAT_GetLongFilename(name);
+
+ for (int r = 0; r < num; r++) {
+ char str[128];
+
+
+ sprintf(str, "%s%02d", prefix, r);
+// consolePrintf("%s != %s", str, name);
+ if (!stricmp(str, name)) {
+ marks[r] = true;
+// consolePrintf("Matched %d", r);
+ }
+
+ }
+
+ }
+
+ } while ((fileType = FAT_FindNextFile(name)));
+
+ FAT_chdir("/");
+}
diff --git a/backends/platform/ds/arm9/source/gbampsave.h b/backends/platform/ds/arm9/source/gbampsave.h
new file mode 100644
index 0000000000..e454b49d4c
--- /dev/null
+++ b/backends/platform/ds/arm9/source/gbampsave.h
@@ -0,0 +1,164 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _GBAMPSAVE_H_
+#define _GBAMPSAVE_H_
+
+#include "stdafx.h"
+#include "system.h"
+
+#define SAVE_BUFFER_SIZE 100000
+
+class GBAMPSaveFile : public Common::SaveFile {
+ FILE* handle;
+ char buffer[SAVE_BUFFER_SIZE];
+ int bufferPos;
+ int saveSize;
+ int flushed;
+
+public:
+ GBAMPSaveFile(char* name, bool saveOrLoad);
+ ~GBAMPSaveFile();
+
+ virtual uint32 read(void *buf, uint32 size);
+ virtual uint32 write(const void *buf, uint32 size);
+
+ virtual bool eos() const;
+ virtual void skip(uint32 bytes);
+
+ virtual uint32 pos() const;
+ virtual uint32 size() const;
+ virtual void seek(int32 pos, int whence);
+
+ void flushSaveBuffer();
+
+ virtual bool isOpen() const {
+ return true;
+ }
+};
+
+
+class GBAMPSaveFileManager : public Common::SaveFileManager {
+
+
+public:
+ GBAMPSaveFileManager();
+ ~GBAMPSaveFileManager();
+
+// static GBAMPSaveFileManager* instance() { return instancePtr; }
+
+ Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad);
+
+ virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); }
+ virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); }
+
+
+ void listSavefiles(const char *prefix, bool *marks, int num);
+
+ void deleteFile(char* name);
+ void listFiles();
+
+protected:
+ Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad);
+};
+
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _GBAMPSAVE_H_
+#define _GBAMPSAVE_H_
+
+#include "stdafx.h"
+#include "system.h"
+
+#define SAVE_BUFFER_SIZE 100000
+
+class GBAMPSaveFile : public Common::SaveFile {
+ FILE* handle;
+ char buffer[SAVE_BUFFER_SIZE];
+ int bufferPos;
+ int saveSize;
+ int flushed;
+
+public:
+ GBAMPSaveFile(char* name, bool saveOrLoad);
+ ~GBAMPSaveFile();
+
+ virtual uint32 read(void *buf, uint32 size);
+ virtual uint32 write(const void *buf, uint32 size);
+
+ virtual bool eos() const;
+ virtual void skip(uint32 bytes);
+
+ virtual uint32 pos() const;
+ virtual uint32 size() const;
+ virtual void seek(int32 pos, int whence);
+
+ void flushSaveBuffer();
+
+ virtual bool isOpen() const {
+ return true;
+ }
+};
+
+
+class GBAMPSaveFileManager : public Common::SaveFileManager {
+
+
+public:
+ GBAMPSaveFileManager();
+ ~GBAMPSaveFileManager();
+
+// static GBAMPSaveFileManager* instance() { return instancePtr; }
+
+ Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad);
+
+ virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); }
+ virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); }
+
+
+ void listSavefiles(const char *prefix, bool *marks, int num);
+
+ void deleteFile(char* name);
+ void listFiles();
+
+protected:
+ Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad);
+};
+
+
+#endif
diff --git a/backends/platform/ds/arm9/source/mad/readme.txt b/backends/platform/ds/arm9/source/mad/readme.txt
new file mode 100644
index 0000000000..09a2ec07df
--- /dev/null
+++ b/backends/platform/ds/arm9/source/mad/readme.txt
@@ -0,0 +1,2 @@
+Put mad.h here if you are compiling with Madlib support enabled.
+Put mad.h here if you are compiling with Madlib support enabled. \ No newline at end of file
diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp
new file mode 100644
index 0000000000..37a79728c3
--- /dev/null
+++ b/backends/platform/ds/arm9/source/osystem_ds.cpp
@@ -0,0 +1,968 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "stdafx.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "system.h"
+
+#include "common/util.h"
+#include "common/rect.h"
+#include "common/savefile.h"
+
+#include "osystem_ds.h"
+#include "nds.h"
+#include "dsmain.h"
+#include "nds/registers_alt.h"
+#include "config-manager.h"
+#include "common/str.h"
+#include "cdaudio.h"
+#include "graphics/surface.h"
+
+OSystem_DS* OSystem_DS::_instance = NULL;
+
+OSystem_DS::OSystem_DS()
+{
+ eventNum = 0;
+ lastPenFrame = 0;
+ queuePos = 0;
+ _instance = this;
+}
+
+OSystem_DS::~OSystem_DS() {
+}
+
+void OSystem_DS::initBackend() {
+ ConfMan.setInt("autosave_period", 0);
+ ConfMan.setBool("FM_medium_quality", true);
+}
+
+bool OSystem_DS::hasFeature(Feature f) {
+// consolePrintf("hasfeature\n");
+ return (f == kFeatureVirtualKeyboard);
+}
+
+void OSystem_DS::setFeatureState(Feature f, bool enable) {
+// consolePrintf("setfeature f=%d e=%d\n", f, enable);
+ if (f == kFeatureVirtualKeyboard) DS::setKeyboardIcon(enable);
+}
+
+bool OSystem_DS::getFeatureState(Feature f) {
+// consolePrintf("getfeat\n");
+ if (f == kFeatureVirtualKeyboard) return DS::getKeyboardIcon();
+ return false;
+}
+
+const OSystem::GraphicsMode* OSystem_DS::getSupportedGraphicsModes() const {
+ return s_supportedGraphicsModes;
+}
+
+
+int OSystem_DS::getDefaultGraphicsMode() const {
+ return 0;
+}
+
+bool OSystem_DS::setGraphicsMode(int mode) {
+ return true;
+}
+
+bool OSystem_DS::setGraphicsMode(const char *name) {
+// consolePrintf("Set gfx mode %s\n", name);
+ return true;
+}
+
+int OSystem_DS::getGraphicsMode() const {
+ return -1;
+}
+
+void OSystem_DS::initSize(uint width, uint height) {
+// consolePrintf("Set gfx mode %d x %d\n", width, height);
+ DS::setGameSize(width, height);
+}
+
+int16 OSystem_DS::getHeight() {
+ return 200;
+}
+
+int16 OSystem_DS::getWidth() {
+ return 320;
+}
+
+void OSystem_DS::setPalette(const byte *colors, uint start, uint num) {
+// consolePrintf("Set palette %d, %d colours\n", start, num);
+ if (!DS::getIsDisplayMode8Bit()) return;
+
+ for (unsigned int r = start; r < start + num; r++) {
+ int red = *colors;
+ int green = *(colors + 1);
+ int blue = *(colors + 2);
+
+ red >>= 3;
+ green >>= 3;
+ blue >>= 3;
+
+ BG_PALETTE[r] = red | (green << 5) | (blue << 10);
+ if (!DS::getKeyboardEnable()) {
+ BG_PALETTE_SUB[r] = red | (green << 5) | (blue << 10);
+ }
+// if (num == 16) consolePrintf("pal:%d r:%d g:%d b:%d\n", r, red, green, blue);
+
+ colors += 4;
+ }
+}
+
+
+void OSystem_DS::grabPalette(unsigned char *colors, uint start, uint num) {
+// consolePrintf("Grabpalette");
+
+ for (unsigned int r = start; r < start + num; r++) {
+ *colors++ = (BG_PALETTE[r] & 0x001F) << 3;
+ *colors++ = (BG_PALETTE[r] & 0x03E0) >> 5 << 3;
+ *colors++ = (BG_PALETTE[r] & 0x7C00) >> 10 << 3;
+ }
+}
+
+
+void OSystem_DS::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h)
+{
+// consolePrintf("Copy rect %d, %d %d, %d ", x, y, w, h);
+
+ if (w <= 1) return;
+ if (h < 0) return;
+ if (!DS::getIsDisplayMode8Bit()) return;
+
+ u16* bgSub = (u16 *) BG_GFX_SUB;
+ u16* bg = (u16 *) DS::get8BitBackBuffer();
+ u16* src = (u16 *) buf;
+
+ if (DS::getKeyboardEnable()) {
+
+ for (int dy = y; dy < y + h; dy++) {
+ u16* dest = bg + (dy << 8) + (x >> 1);
+
+ DC_FlushRange(src, w << 1);
+ DC_FlushRange(dest, w << 1);
+ dmaCopyHalfWords(3, src, dest, w);
+
+ src += pitch >> 1;
+ }
+
+ } else {
+ for (int dy = y; dy < y + h; dy++) {
+ u16* dest1 = bg + (dy << 8) + (x >> 1);
+ u16* dest2 = bgSub + (dy << 8) + (x >> 1);
+
+ DC_FlushRange(src, w << 1);
+ DC_FlushRange(dest1, w << 1);
+ DC_FlushRange(dest2, w << 1);
+
+ dmaCopyHalfWords(3, src, dest1, w);
+ dmaCopyHalfWords(3, src, dest2, w);
+
+ src += pitch >> 1;
+ }
+ }
+
+// consolePrintf("Done\n");
+
+
+
+}
+
+void OSystem_DS::updateScreen()
+{
+ DS::displayMode16BitFlipBuffer();
+ DS::doSoundCallback();
+// DS::doTimerCallback();
+ DS::addEventsToQueue();
+}
+
+void OSystem_DS::setShakePos(int shakeOffset) {
+ DS::setShakePos(shakeOffset);
+}
+
+void OSystem_DS::showOverlay ()
+{
+// consolePrintf("showovl\n");
+ DS::displayMode16Bit();
+}
+
+void OSystem_DS::hideOverlay ()
+{
+ DS::displayMode8Bit();
+}
+
+void OSystem_DS::clearOverlay ()
+{
+ memset((u16 *) DS::get16BitBackBuffer(), 0, 512 * 256 * 2);
+// consolePrintf("clearovl\n");
+}
+
+void OSystem_DS::grabOverlay (OverlayColor *buf, int pitch)
+{
+// consolePrintf("grabovl\n");
+}
+
+void OSystem_DS::copyRectToOverlay (const OverlayColor *buf, int pitch, int x, int y, int w, int h)
+{
+ u16* bg = (u16 *) DS::get16BitBackBuffer();
+ u16* src = (u16 *) buf;
+
+// if (x + w > 256) w = 256 - x;
+ //if (x + h > 256) h = 256 - y;
+
+// consolePrintf("Copy rect ovl %d, %d %d, %d %d\n", x, y, w, h, pitch);
+
+
+
+ for (int dy = y; dy < y + h; dy++) {
+
+
+ // Slow but save copy:
+ for (int dx = x; dx < x + w; dx++) {
+
+ *(bg + (dy * 512) + dx) = *src;
+ //if ((*src) != 0) consolePrintf("%d,%d: %d ", dx, dy, *src);
+ //consolePrintf("%d,", *src);
+ src++;
+ }
+ src += (pitch - w);
+
+ // Fast but broken copy: (why?)
+ /*
+ REG_IME = 0;
+ dmaCopy(src, bg + (dy << 9) + x, w * 2);
+ REG_IME = 1;
+
+ src += pitch;*/
+ }
+
+// consolePrintf("Copy rect ovl done");
+
+}
+
+int16 OSystem_DS::getOverlayHeight()
+{
+// consolePrintf("getovlheight\n");
+ return getHeight();
+}
+
+int16 OSystem_DS::getOverlayWidth()
+{
+// consolePrintf("getovlwid\n");
+ return getWidth();
+}
+
+
+bool OSystem_DS::showMouse(bool visible)
+{
+ return true;
+}
+
+void OSystem_DS::warpMouse(int x, int y)
+{
+}
+
+void OSystem_DS::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetCursorScale) {
+ DS::setCursorIcon(buf, w, h, keycolor);
+}
+
+void OSystem_DS::addEvent(Event& e) {
+ eventQueue[queuePos++] = e;
+}
+
+bool OSystem_DS::pollEvent(Event &event)
+{
+
+ if (lastPenFrame != DS::getMillis()) {
+
+ if (eventNum == queuePos) {
+ eventNum = 0;
+ queuePos = 0;
+ // Bodge - this last event seems to be processed sometimes and not others.
+ // So we make it something harmless which won't cause any adverse effects.
+ event.type = EVENT_KEYUP;
+ event.kbd.ascii = 0;
+ event.kbd.keycode = 0;
+ event.kbd.flags = 0;
+ return false;
+ } else {
+ event = eventQueue[eventNum++];
+ return true;
+ }
+ }
+
+ return false;
+
+/* if (lastPenFrame != DS::getMillis()) {
+ if ((eventNum == 0)) {
+ event.type = EVENT_MOUSEMOVE;
+ event.mouse = Common::Point(DS::getPenX(), DS::getPenY());
+ eventNum = 1;
+ return true;
+ }
+ if (eventNum == 1) {
+ eventNum = 0;
+ lastPenFrame = DS::getMillis();
+ if (DS::getPenDown()) {
+ event.type = EVENT_LBUTTONDOWN;
+ event.mouse = Common::Point(DS::getPenX(), DS::getPenY());
+ consolePrintf("Down %d, %d ", event.mouse.x, event.mouse.y);
+ return true;
+ } else if (DS::getPenReleased()) {
+ event.type = EVENT_LBUTTONUP;
+ event.mouse = Common::Point(DS::getPenX(), DS::getPenY());
+ consolePrintf("Up %d, %d ", event.mouse.x, event.mouse.y);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }*/
+
+ return false;
+}
+
+uint32 OSystem_DS::getMillis()
+{
+ return DS::getMillis();
+}
+
+void OSystem_DS::delayMillis(uint msecs)
+{
+ int st = getMillis();
+ DS::addEventsToQueue();
+ DS::CD::update();
+
+ DS::doSoundCallback();
+ while (st + msecs >= getMillis()) {
+ DS::doSoundCallback();
+ }
+
+ DS::doTimerCallback();
+ DS::checkSleepMode();
+ DS::addEventsToQueue();
+}
+
+void OSystem_DS::setTimerCallback(TimerProc callback, int interval)
+{
+// consolePrintf("Settimercallback interval=%d\n", interval);
+ DS::setTimerCallback(callback, interval);
+}
+
+OSystem::MutexRef OSystem_DS::createMutex(void)
+{
+ return NULL;
+}
+
+void OSystem_DS::lockMutex(MutexRef mutex)
+{
+}
+
+void OSystem_DS::unlockMutex(MutexRef mutex)
+{
+}
+
+void OSystem_DS::deleteMutex(MutexRef mutex)
+{
+}
+
+bool OSystem_DS::setSoundCallback(SoundProc proc, void *param)
+{
+// consolePrintf("Setsoundcallback");
+ DS::setSoundProc(proc, param);
+ return true;
+}
+
+void OSystem_DS::clearSoundCallback()
+{
+ consolePrintf("Clearing sound callback");
+// DS::setSoundProc(NULL, NULL);
+}
+
+int OSystem_DS::getOutputSampleRate() const
+{
+ return 11025;
+}
+
+bool OSystem_DS::openCD(int drive)
+{
+ return DS::CD::checkCD();
+}
+
+bool OSystem_DS::pollCD()
+{
+ return DS::CD::isPlaying();
+}
+
+void OSystem_DS::playCD(int track, int num_loops, int start_frame, int duration)
+{
+ DS::CD::playTrack(track, num_loops, start_frame, duration);
+}
+
+void OSystem_DS::stopCD()
+{
+ DS::CD::stopTrack();
+}
+
+void OSystem_DS::updateCD()
+{
+}
+
+void OSystem_DS::quit()
+{
+/* consolePrintf("Soft resetting...");
+ IPC->reset = 1;
+ REG_IE = 0;
+
+ asm("swi 0x26\n");
+ swiSoftReset();*/
+}
+
+void OSystem_DS::setWindowCaption(const char *caption)
+{
+}
+
+void OSystem_DS::displayMessageOnOSD(const char *msg)
+{
+}
+
+Common::SaveFileManager* OSystem_DS::getSavefileManager()
+{
+ bool forceSram;
+
+ if (ConfMan.hasKey("forcesramsave", "ds")) {
+ forceSram = ConfMan.getBool("forcesramsave", "ds");
+ } else {
+ forceSram = false;
+ }
+ if (forceSram) {
+ consolePrintf("Using SRAM save method!\n");
+ }
+
+ if (DS::isGBAMPAvailable() && (!forceSram)) {
+ return &mpSaveManager;
+ } else {
+ return &saveManager;
+ }
+}
+
+bool OSystem_DS::grabRawScreen(Graphics::Surface* surf) {
+ surf->create(DS::getGameWidth(), DS::getGameHeight(), 1);
+ memcpy(surf->pixels, DS::get8BitBackBuffer(), DS::getGameWidth() * DS::getGameHeight());
+ return true;
+}
+
+void OSystem_DS::setFocusRectangle(const Common::Rect& rect) {
+ DS::setTalkPos(rect.left + rect.width() / 2, rect.top + rect.height() / 2);
+}
+
+void OSystem_DS::clearFocusRectangle() {
+
+}
+
+
+OSystem *OSystem_DS_create() {
+ return new OSystem_DS();
+}
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "stdafx.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "system.h"
+
+#include "common/util.h"
+#include "common/rect.h"
+#include "common/savefile.h"
+
+#include "osystem_ds.h"
+#include "nds.h"
+#include "dsmain.h"
+#include "nds/registers_alt.h"
+#include "config-manager.h"
+#include "common/str.h"
+#include "cdaudio.h"
+#include "graphics/surface.h"
+
+OSystem_DS* OSystem_DS::_instance = NULL;
+
+OSystem_DS::OSystem_DS()
+{
+ eventNum = 0;
+ lastPenFrame = 0;
+ queuePos = 0;
+ _instance = this;
+}
+
+OSystem_DS::~OSystem_DS() {
+}
+
+void OSystem_DS::initBackend() {
+ ConfMan.setInt("autosave_period", 0);
+ ConfMan.setBool("FM_medium_quality", true);
+}
+
+bool OSystem_DS::hasFeature(Feature f) {
+// consolePrintf("hasfeature\n");
+ return (f == kFeatureVirtualKeyboard);
+}
+
+void OSystem_DS::setFeatureState(Feature f, bool enable) {
+// consolePrintf("setfeature f=%d e=%d\n", f, enable);
+ if (f == kFeatureVirtualKeyboard) DS::setKeyboardIcon(enable);
+}
+
+bool OSystem_DS::getFeatureState(Feature f) {
+// consolePrintf("getfeat\n");
+ if (f == kFeatureVirtualKeyboard) return DS::getKeyboardIcon();
+ return false;
+}
+
+const OSystem::GraphicsMode* OSystem_DS::getSupportedGraphicsModes() const {
+ return s_supportedGraphicsModes;
+}
+
+
+int OSystem_DS::getDefaultGraphicsMode() const {
+ return 0;
+}
+
+bool OSystem_DS::setGraphicsMode(int mode) {
+ return true;
+}
+
+bool OSystem_DS::setGraphicsMode(const char *name) {
+// consolePrintf("Set gfx mode %s\n", name);
+ return true;
+}
+
+int OSystem_DS::getGraphicsMode() const {
+ return -1;
+}
+
+void OSystem_DS::initSize(uint width, uint height) {
+// consolePrintf("Set gfx mode %d x %d\n", width, height);
+ DS::setGameSize(width, height);
+}
+
+int16 OSystem_DS::getHeight() {
+ return 200;
+}
+
+int16 OSystem_DS::getWidth() {
+ return 320;
+}
+
+void OSystem_DS::setPalette(const byte *colors, uint start, uint num) {
+// consolePrintf("Set palette %d, %d colours\n", start, num);
+ if (!DS::getIsDisplayMode8Bit()) return;
+
+ for (unsigned int r = start; r < start + num; r++) {
+ int red = *colors;
+ int green = *(colors + 1);
+ int blue = *(colors + 2);
+
+ red >>= 3;
+ green >>= 3;
+ blue >>= 3;
+
+ BG_PALETTE[r] = red | (green << 5) | (blue << 10);
+ if (!DS::getKeyboardEnable()) {
+ BG_PALETTE_SUB[r] = red | (green << 5) | (blue << 10);
+ }
+// if (num == 16) consolePrintf("pal:%d r:%d g:%d b:%d\n", r, red, green, blue);
+
+ colors += 4;
+ }
+}
+
+
+void OSystem_DS::grabPalette(unsigned char *colors, uint start, uint num) {
+// consolePrintf("Grabpalette");
+
+ for (unsigned int r = start; r < start + num; r++) {
+ *colors++ = (BG_PALETTE[r] & 0x001F) << 3;
+ *colors++ = (BG_PALETTE[r] & 0x03E0) >> 5 << 3;
+ *colors++ = (BG_PALETTE[r] & 0x7C00) >> 10 << 3;
+ }
+}
+
+
+void OSystem_DS::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h)
+{
+// consolePrintf("Copy rect %d, %d %d, %d ", x, y, w, h);
+
+ if (w <= 1) return;
+ if (h < 0) return;
+ if (!DS::getIsDisplayMode8Bit()) return;
+
+ u16* bgSub = (u16 *) BG_GFX_SUB;
+ u16* bg = (u16 *) DS::get8BitBackBuffer();
+ u16* src = (u16 *) buf;
+
+ if (DS::getKeyboardEnable()) {
+
+ for (int dy = y; dy < y + h; dy++) {
+ u16* dest = bg + (dy << 8) + (x >> 1);
+
+ DC_FlushRange(src, w << 1);
+ DC_FlushRange(dest, w << 1);
+ dmaCopyHalfWords(3, src, dest, w);
+
+ src += pitch >> 1;
+ }
+
+ } else {
+ for (int dy = y; dy < y + h; dy++) {
+ u16* dest1 = bg + (dy << 8) + (x >> 1);
+ u16* dest2 = bgSub + (dy << 8) + (x >> 1);
+
+ DC_FlushRange(src, w << 1);
+ DC_FlushRange(dest1, w << 1);
+ DC_FlushRange(dest2, w << 1);
+
+ dmaCopyHalfWords(3, src, dest1, w);
+ dmaCopyHalfWords(3, src, dest2, w);
+
+ src += pitch >> 1;
+ }
+ }
+
+// consolePrintf("Done\n");
+
+
+
+}
+
+void OSystem_DS::updateScreen()
+{
+ DS::displayMode16BitFlipBuffer();
+ DS::doSoundCallback();
+// DS::doTimerCallback();
+ DS::addEventsToQueue();
+}
+
+void OSystem_DS::setShakePos(int shakeOffset) {
+ DS::setShakePos(shakeOffset);
+}
+
+void OSystem_DS::showOverlay ()
+{
+// consolePrintf("showovl\n");
+ DS::displayMode16Bit();
+}
+
+void OSystem_DS::hideOverlay ()
+{
+ DS::displayMode8Bit();
+}
+
+void OSystem_DS::clearOverlay ()
+{
+ memset((u16 *) DS::get16BitBackBuffer(), 0, 512 * 256 * 2);
+// consolePrintf("clearovl\n");
+}
+
+void OSystem_DS::grabOverlay (OverlayColor *buf, int pitch)
+{
+// consolePrintf("grabovl\n");
+}
+
+void OSystem_DS::copyRectToOverlay (const OverlayColor *buf, int pitch, int x, int y, int w, int h)
+{
+ u16* bg = (u16 *) DS::get16BitBackBuffer();
+ u16* src = (u16 *) buf;
+
+// if (x + w > 256) w = 256 - x;
+ //if (x + h > 256) h = 256 - y;
+
+// consolePrintf("Copy rect ovl %d, %d %d, %d %d\n", x, y, w, h, pitch);
+
+
+
+ for (int dy = y; dy < y + h; dy++) {
+
+
+ // Slow but save copy:
+ for (int dx = x; dx < x + w; dx++) {
+
+ *(bg + (dy * 512) + dx) = *src;
+ //if ((*src) != 0) consolePrintf("%d,%d: %d ", dx, dy, *src);
+ //consolePrintf("%d,", *src);
+ src++;
+ }
+ src += (pitch - w);
+
+ // Fast but broken copy: (why?)
+ /*
+ REG_IME = 0;
+ dmaCopy(src, bg + (dy << 9) + x, w * 2);
+ REG_IME = 1;
+
+ src += pitch;*/
+ }
+
+// consolePrintf("Copy rect ovl done");
+
+}
+
+int16 OSystem_DS::getOverlayHeight()
+{
+// consolePrintf("getovlheight\n");
+ return getHeight();
+}
+
+int16 OSystem_DS::getOverlayWidth()
+{
+// consolePrintf("getovlwid\n");
+ return getWidth();
+}
+
+
+bool OSystem_DS::showMouse(bool visible)
+{
+ return true;
+}
+
+void OSystem_DS::warpMouse(int x, int y)
+{
+}
+
+void OSystem_DS::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetCursorScale) {
+ DS::setCursorIcon(buf, w, h, keycolor);
+}
+
+void OSystem_DS::addEvent(Event& e) {
+ eventQueue[queuePos++] = e;
+}
+
+bool OSystem_DS::pollEvent(Event &event)
+{
+
+ if (lastPenFrame != DS::getMillis()) {
+
+ if (eventNum == queuePos) {
+ eventNum = 0;
+ queuePos = 0;
+ // Bodge - this last event seems to be processed sometimes and not others.
+ // So we make it something harmless which won't cause any adverse effects.
+ event.type = EVENT_KEYUP;
+ event.kbd.ascii = 0;
+ event.kbd.keycode = 0;
+ event.kbd.flags = 0;
+ return false;
+ } else {
+ event = eventQueue[eventNum++];
+ return true;
+ }
+ }
+
+ return false;
+
+/* if (lastPenFrame != DS::getMillis()) {
+ if ((eventNum == 0)) {
+ event.type = EVENT_MOUSEMOVE;
+ event.mouse = Common::Point(DS::getPenX(), DS::getPenY());
+ eventNum = 1;
+ return true;
+ }
+ if (eventNum == 1) {
+ eventNum = 0;
+ lastPenFrame = DS::getMillis();
+ if (DS::getPenDown()) {
+ event.type = EVENT_LBUTTONDOWN;
+ event.mouse = Common::Point(DS::getPenX(), DS::getPenY());
+ consolePrintf("Down %d, %d ", event.mouse.x, event.mouse.y);
+ return true;
+ } else if (DS::getPenReleased()) {
+ event.type = EVENT_LBUTTONUP;
+ event.mouse = Common::Point(DS::getPenX(), DS::getPenY());
+ consolePrintf("Up %d, %d ", event.mouse.x, event.mouse.y);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }*/
+
+ return false;
+}
+
+uint32 OSystem_DS::getMillis()
+{
+ return DS::getMillis();
+}
+
+void OSystem_DS::delayMillis(uint msecs)
+{
+ int st = getMillis();
+ DS::addEventsToQueue();
+ DS::CD::update();
+
+ DS::doSoundCallback();
+ while (st + msecs >= getMillis()) {
+ DS::doSoundCallback();
+ }
+
+ DS::doTimerCallback();
+ DS::checkSleepMode();
+ DS::addEventsToQueue();
+}
+
+void OSystem_DS::setTimerCallback(TimerProc callback, int interval)
+{
+// consolePrintf("Settimercallback interval=%d\n", interval);
+ DS::setTimerCallback(callback, interval);
+}
+
+OSystem::MutexRef OSystem_DS::createMutex(void)
+{
+ return NULL;
+}
+
+void OSystem_DS::lockMutex(MutexRef mutex)
+{
+}
+
+void OSystem_DS::unlockMutex(MutexRef mutex)
+{
+}
+
+void OSystem_DS::deleteMutex(MutexRef mutex)
+{
+}
+
+bool OSystem_DS::setSoundCallback(SoundProc proc, void *param)
+{
+// consolePrintf("Setsoundcallback");
+ DS::setSoundProc(proc, param);
+ return true;
+}
+
+void OSystem_DS::clearSoundCallback()
+{
+ consolePrintf("Clearing sound callback");
+// DS::setSoundProc(NULL, NULL);
+}
+
+int OSystem_DS::getOutputSampleRate() const
+{
+ return 11025;
+}
+
+bool OSystem_DS::openCD(int drive)
+{
+ return DS::CD::checkCD();
+}
+
+bool OSystem_DS::pollCD()
+{
+ return DS::CD::isPlaying();
+}
+
+void OSystem_DS::playCD(int track, int num_loops, int start_frame, int duration)
+{
+ DS::CD::playTrack(track, num_loops, start_frame, duration);
+}
+
+void OSystem_DS::stopCD()
+{
+ DS::CD::stopTrack();
+}
+
+void OSystem_DS::updateCD()
+{
+}
+
+void OSystem_DS::quit()
+{
+/* consolePrintf("Soft resetting...");
+ IPC->reset = 1;
+ REG_IE = 0;
+
+ asm("swi 0x26\n");
+ swiSoftReset();*/
+}
+
+void OSystem_DS::setWindowCaption(const char *caption)
+{
+}
+
+void OSystem_DS::displayMessageOnOSD(const char *msg)
+{
+}
+
+Common::SaveFileManager* OSystem_DS::getSavefileManager()
+{
+ bool forceSram;
+
+ if (ConfMan.hasKey("forcesramsave", "ds")) {
+ forceSram = ConfMan.getBool("forcesramsave", "ds");
+ } else {
+ forceSram = false;
+ }
+ if (forceSram) {
+ consolePrintf("Using SRAM save method!\n");
+ }
+
+ if (DS::isGBAMPAvailable() && (!forceSram)) {
+ return &mpSaveManager;
+ } else {
+ return &saveManager;
+ }
+}
+
+bool OSystem_DS::grabRawScreen(Graphics::Surface* surf) {
+ surf->create(DS::getGameWidth(), DS::getGameHeight(), 1);
+ memcpy(surf->pixels, DS::get8BitBackBuffer(), DS::getGameWidth() * DS::getGameHeight());
+ return true;
+}
+
+void OSystem_DS::setFocusRectangle(Common::Rect& rect) {
+ DS::setTalkPos(rect.left + rect.width() / 2, rect.top + rect.height() / 2);
+}
+
+void OSystem_DS::clearFocusRectangle() {
+
+}
+
+
+OSystem *OSystem_DS_create() {
+ return new OSystem_DS();
+}
+
diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h
new file mode 100644
index 0000000000..fc108db670
--- /dev/null
+++ b/backends/platform/ds/arm9/source/osystem_ds.h
@@ -0,0 +1,284 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _OSYSTEM_DS_H_
+#define _OSYSTEM_DS_H_
+#include "common/system.h"
+#include "nds.h"
+#include "ramsave.h"
+#include "gbampsave.h"
+
+class OSystem_DS : public OSystem {
+public:
+ static OSystem_DS *instance() { return _instance; }
+ int eventNum;
+ int lastPenFrame;
+
+ Event eventQueue[64];
+ int queuePos;
+
+ DSSaveFileManager saveManager;
+ GBAMPSaveFileManager mpSaveManager;
+
+ static OSystem_DS* _instance;
+
+public:
+
+ OSystem_DS();
+ virtual ~OSystem_DS();
+
+ virtual bool hasFeature(Feature f);
+ virtual void setFeatureState(Feature f, bool enable);
+ virtual bool getFeatureState(Feature f);
+ virtual const GraphicsMode *getSupportedGraphicsModes() const;
+ virtual int getDefaultGraphicsMode() const;
+ virtual bool setGraphicsMode(int mode);
+ bool setGraphicsMode(const char *name);
+ virtual int getGraphicsMode() const;
+ virtual void initSize(uint width, uint height);
+ virtual int16 getHeight();
+ virtual int16 getWidth();
+ virtual void setPalette(const byte *colors, uint start, uint num);
+ virtual void grabPalette(unsigned char* colors, uint start, uint num);
+
+ virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
+ virtual void updateScreen();
+ virtual void setShakePos(int shakeOffset);
+
+ virtual void showOverlay();
+ virtual void hideOverlay();
+ virtual void clearOverlay();
+ virtual void grabOverlay(OverlayColor *buf, int pitch);
+ virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h);
+ virtual int16 getOverlayHeight();
+ virtual int16 getOverlayWidth();
+
+ inline virtual OverlayColor RGBToColor(uint8 r, uint8 g, uint8 b);
+ inline virtual void colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b);
+
+ virtual bool showMouse(bool visible);
+
+ virtual void warpMouse(int x, int y);
+ virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetCursorScale = 1);
+
+ virtual bool pollEvent(Event &event);
+ virtual uint32 getMillis();
+ virtual void delayMillis(uint msecs);
+
+ virtual void setTimerCallback(TimerProc callback, int interval);
+
+ virtual MutexRef createMutex(void);
+ virtual void lockMutex(MutexRef mutex);
+ virtual void unlockMutex(MutexRef mutex);
+ virtual void deleteMutex(MutexRef mutex);
+
+ virtual bool setSoundCallback(SoundProc proc, void *param);
+ virtual void clearSoundCallback();
+ virtual int getOutputSampleRate() const;
+
+ virtual bool openCD(int drive);
+ virtual bool pollCD();
+
+ virtual void playCD(int track, int num_loops, int start_frame, int duration);
+ virtual void stopCD();
+ virtual void updateCD();
+
+ virtual void quit();
+
+ virtual void setWindowCaption(const char *caption);
+
+ virtual void displayMessageOnOSD(const char *msg);
+
+ virtual Common::SaveFileManager *getSavefileManager();
+
+ void addEvent(Event& e);
+ bool isEventQueueEmpty() { return queuePos == 0; }
+
+ virtual bool grabRawScreen(Graphics::Surface* surf);
+
+ virtual void setFocusRectangle(const Common::Rect& rect);
+
+ virtual void clearFocusRectangle();
+
+ virtual void initBackend();
+};
+
+static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
+ {0, 0, 0},
+};
+
+OverlayColor OSystem_DS::RGBToColor(uint8 r, uint8 g, uint8 b)
+{
+ return 0x8000 | (r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 5);
+ //consolePrintf("rgbtocol\n");
+ return 0;
+}
+
+void OSystem_DS::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b)
+{
+ r = (color & 0x001F) << 3;
+ g = (color & 0x03E0) >> 5 << 3;
+ b = (color & 0x7C00) >> 10 << 3;
+ //consolePrintf("coltorgb\n");
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _OSYSTEM_DS_H_
+#define _OSYSTEM_DS_H_
+#include "common/system.h"
+#include "nds.h"
+#include "ramsave.h"
+#include "gbampsave.h"
+
+class OSystem_DS : public OSystem {
+public:
+ static OSystem_DS *instance() { return _instance; }
+ int eventNum;
+ int lastPenFrame;
+
+ Event eventQueue[64];
+ int queuePos;
+
+ DSSaveFileManager saveManager;
+ GBAMPSaveFileManager mpSaveManager;
+
+ static OSystem_DS* _instance;
+
+public:
+
+ OSystem_DS();
+ virtual ~OSystem_DS();
+
+ virtual bool hasFeature(Feature f);
+ virtual void setFeatureState(Feature f, bool enable);
+ virtual bool getFeatureState(Feature f);
+ virtual const GraphicsMode *getSupportedGraphicsModes() const;
+ virtual int getDefaultGraphicsMode() const;
+ virtual bool setGraphicsMode(int mode);
+ bool setGraphicsMode(const char *name);
+ virtual int getGraphicsMode() const;
+ virtual void initSize(uint width, uint height);
+ virtual int16 getHeight();
+ virtual int16 getWidth();
+ virtual void setPalette(const byte *colors, uint start, uint num);
+ virtual void grabPalette(unsigned char* colors, uint start, uint num);
+
+ virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
+ virtual void updateScreen();
+ virtual void setShakePos(int shakeOffset);
+
+ virtual void showOverlay();
+ virtual void hideOverlay();
+ virtual void clearOverlay();
+ virtual void grabOverlay(OverlayColor *buf, int pitch);
+ virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h);
+ virtual int16 getOverlayHeight();
+ virtual int16 getOverlayWidth();
+
+ inline virtual OverlayColor RGBToColor(uint8 r, uint8 g, uint8 b);
+ inline virtual void colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b);
+
+ virtual bool showMouse(bool visible);
+
+ virtual void warpMouse(int x, int y);
+ virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetCursorScale = 1);
+
+ virtual bool pollEvent(Event &event);
+ virtual uint32 getMillis();
+ virtual void delayMillis(uint msecs);
+
+ virtual void setTimerCallback(TimerProc callback, int interval);
+
+ virtual MutexRef createMutex(void);
+ virtual void lockMutex(MutexRef mutex);
+ virtual void unlockMutex(MutexRef mutex);
+ virtual void deleteMutex(MutexRef mutex);
+
+ virtual bool setSoundCallback(SoundProc proc, void *param);
+ virtual void clearSoundCallback();
+ virtual int getOutputSampleRate() const;
+
+ virtual bool openCD(int drive);
+ virtual bool pollCD();
+
+ virtual void playCD(int track, int num_loops, int start_frame, int duration);
+ virtual void stopCD();
+ virtual void updateCD();
+
+ virtual void quit();
+
+ virtual void setWindowCaption(const char *caption);
+
+ virtual void displayMessageOnOSD(const char *msg);
+
+ virtual Common::SaveFileManager *getSavefileManager();
+
+ void addEvent(Event& e);
+ bool isEventQueueEmpty() { return queuePos == 0; }
+
+ virtual bool grabRawScreen(Graphics::Surface* surf);
+
+ virtual void setFocusRectangle(Common::Rect& rect);
+
+ virtual void clearFocusRectangle();
+
+ virtual void initBackend();
+};
+
+static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
+ {0, 0, 0},
+};
+
+OverlayColor OSystem_DS::RGBToColor(uint8 r, uint8 g, uint8 b)
+{
+ return 0x8000 | (r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 5);
+ //consolePrintf("rgbtocol\n");
+ return 0;
+}
+
+void OSystem_DS::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b)
+{
+ r = (color & 0x001F) << 3;
+ g = (color & 0x03E0) >> 5 << 3;
+ b = (color & 0x7C00) >> 10 << 3;
+ //consolePrintf("coltorgb\n");
+}
+
+#endif
diff --git a/backends/platform/ds/arm9/source/portdefs.cpp b/backends/platform/ds/arm9/source/portdefs.cpp
new file mode 100644
index 0000000000..b9d107a4db
--- /dev/null
+++ b/backends/platform/ds/arm9/source/portdefs.cpp
@@ -0,0 +1,84 @@
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "portdefs.h"
+#include <string.h>
+#include "nds/dma.h"
+#include "osystem_ds.h"
+
+time_t DS_time(time_t) {
+ if (OSystem_DS::instance()) {
+ return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000);
+ } else {
+ return 0xABCD1234;
+ }
+}
+
+time_t DS_time(long* t) {
+ if (OSystem_DS::instance()) {
+ if (t) *t = 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000);
+ return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000);
+ } else {
+ if (t) *t = 0xABCD1234;
+ return 0xABCD1234;
+ }
+}
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "portdefs.h"
+#include <string.h>
+#include "nds/dma.h"
+#include "osystem_ds.h"
+
+time_t DS_time(time_t) {
+ if (OSystem_DS::instance()) {
+ return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000);
+ } else {
+ return 0xABCD1234;
+ }
+}
+
+time_t DS_time(long* t) {
+ if (OSystem_DS::instance()) {
+ if (t) *t = 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000);
+ return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000);
+ } else {
+ if (t) *t = 0xABCD1234;
+ return 0xABCD1234;
+ }
+}
diff --git a/backends/platform/ds/arm9/source/portdefs.h b/backends/platform/ds/arm9/source/portdefs.h
new file mode 100644
index 0000000000..1683583432
--- /dev/null
+++ b/backends/platform/ds/arm9/source/portdefs.h
@@ -0,0 +1,100 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _PORTDEFS_H_
+#define _PORTDEFS_H_
+
+
+typedef unsigned char u8;
+typedef signed char s8;
+
+typedef unsigned short u16;
+typedef signed short s16;
+
+typedef unsigned int u32;
+typedef signed int s32;
+
+//#define double float
+
+
+#undef assert
+#define assert(expr) consolePrintf("Asserted!")
+//#define NO_DEBUG_MSGS
+#include "ds-fs.h"
+
+//#define debug(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
+//#define debug(fmt, ...) debug(0, fmt, ##__VA_ARGS__)
+#define time(t) DS_time(t)
+//#define memcpy(dest, src, size) DS_memcpy(dest, src, size)
+
+time_t DS_time(time_t* t);
+time_t DS_time(long* t);
+void* DS_memcpy(void* s1, void const* s2, size_t n);
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _PORTDEFS_H_
+#define _PORTDEFS_H_
+
+
+typedef unsigned char u8;
+typedef signed char s8;
+
+typedef unsigned short u16;
+typedef signed short s16;
+
+typedef unsigned int u32;
+typedef signed int s32;
+
+//#define double float
+
+
+#undef assert
+#define assert(expr) consolePrintf("Asserted!")
+//#define NO_DEBUG_MSGS
+#include "ds-fs.h"
+
+//#define debug(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
+//#define debug(fmt, ...) debug(0, fmt, ##__VA_ARGS__)
+#define time(t) DS_time(t)
+//#define memcpy(dest, src, size) DS_memcpy(dest, src, size)
+
+time_t DS_time(time_t* t);
+time_t DS_time(long* t);
+void* DS_memcpy(void* s1, void const* s2, size_t n);
+
+#endif
diff --git a/backends/platform/ds/arm9/source/ramsave.cpp b/backends/platform/ds/arm9/source/ramsave.cpp
new file mode 100644
index 0000000000..8a23baff73
--- /dev/null
+++ b/backends/platform/ds/arm9/source/ramsave.cpp
@@ -0,0 +1,918 @@
+/* Ramsave
+ * Copyright (C) 2002-2004 Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+ // Save in order 1,2,3,4,larger 2,5
+#include "stdafx.h"
+#include "system.h"
+#include "ramsave.h"
+#include "nds.h"
+#include "compressor/lz.h"
+
+#define CART_RAM ((vu8 *) (0x0A000000))
+
+DSSaveFile::DSSaveFile() {
+ ptr = 0;
+ saveCompressed = false;
+ save.isValid = false;
+ ownsData = false;
+ isOpenFlag = true;
+ isTempFile = false;
+}
+
+DSSaveFile::DSSaveFile(SCUMMSave* s, bool compressed, u8* data) {
+ save = *s;
+ saveData = data;
+ ptr = 0;
+ saveCompressed = compressed;
+ isOpenFlag = true;
+
+ if (saveCompressed) {
+ u8* uncompressed = new unsigned char[save.size];
+ if (!uncompressed) consolePrintf("Out of memory allocating %d!\n", save.size);
+ LZ_Uncompress(saveData, uncompressed, save.compressedSize);
+ saveData = uncompressed;
+ ownsData = true;
+ saveCompressed = false;
+// consolePrintf("Decompressed. name=%s size=%d (%d)", save.name, save.size, save.compressedSize);
+
+ } else {
+ ownsData = false;
+ origHeader = s;
+ }
+
+ if (save.magic == (int) 0xBEEFCAFE) {
+ save.isValid = true;
+ } else {
+ save.isValid = false;
+ }
+
+ isTempFile = false;
+}
+
+DSSaveFile::~DSSaveFile() {
+ if (!ownsData) {
+ *origHeader = save;
+ DSSaveFileManager::instance()->flushToSaveRAM();
+ }
+ if (ownsData) {
+ delete saveData;
+ }
+}
+
+bool DSSaveFile::loadFromSaveRAM(vu8* address) {
+
+ SCUMMSave newSave;
+
+ for (int t = 0; t < (int) sizeof(newSave); t++) {
+ ((char *) (&newSave))[t] = *(address + t);
+ }
+
+
+ if (newSave.magic == 0xBEEFCAFE) {
+ newSave.isValid = true;
+
+ *((u16 *) (0x4000204)) |= 0x3;
+
+ saveData = new unsigned char[newSave.compressedSize];
+
+ for (int t = 0; t < (int) newSave.compressedSize; t++) {
+ ((char *) (saveData))[t] = *(address + t + sizeof(newSave));
+ }
+
+ if (ownsData) delete this->saveData;
+ save = newSave;
+ saveCompressed = true;
+ this->saveData = saveData;
+ ownsData = true;
+ ptr = 0;
+ return true;
+ }
+
+ return false;
+}
+
+void DSSaveFile::compress() {
+ if (!saveCompressed) {
+ unsigned char* compBuffer = new unsigned char[(save.size * 110) / 100];
+ int compSize = LZ_Compress((u8 *) saveData, compBuffer, save.size);
+ save.compressedSize = compSize;
+
+
+
+ delete saveData;
+
+ // Make the save smaller
+ saveData = (u8 *) realloc(compBuffer, save.compressedSize);
+ saveCompressed = true;
+ }
+}
+
+int DSSaveFile::saveToSaveRAM(vu8* address) {
+
+ unsigned char* compBuffer;
+ bool failed;
+
+
+ int compSize;
+
+ compress();
+
+ compSize = save.compressedSize;
+ compBuffer = saveData;
+
+ if (DSSaveFileManager::instance()->getBytesFree() >= getRamUsage()) {
+
+ DSSaveFileManager::instance()->addBytesFree(-getRamUsage());
+
+ // Write header
+ for (int t = 0; t < sizeof(save); t++) {
+ while (*(address + t) != ((char *) (&save))[t]) {
+ *(address + t) = ((char *) (&save))[t];
+ }
+ }
+
+ // Write compressed buffer
+ for (int t = sizeof(save); t < (int) sizeof(save) + compSize; t++) {
+ while (*(address + t) != compBuffer[t - sizeof(save)]) {
+ *(address + t) = compBuffer[t - sizeof(save)];
+ }
+ }
+
+ failed = false;
+ } else {
+ failed = true;
+ }
+
+
+ return failed? 0: compSize + sizeof(save);
+
+}
+
+void DSSaveFile::reset() {
+ ptr = 0;
+}
+
+uint32 DSSaveFile::read(void *buf, uint32 size) {
+ if (ptr + size > save.size) {
+ size = save.size - ptr;
+ if (size < 0) size = 0;
+ }
+ memcpy(buf, saveData + ptr, size);
+// consolePrintf("byte: %d ", ((u8 *) (buf))[0]);
+
+ ptr += size;
+ return size;
+}
+
+uint32 DSSaveFile::pos() const {
+ return ptr;
+}
+
+uint32 DSSaveFile::size() const {
+ return save.size;
+}
+
+void DSSaveFile::seek(int32 pos, int whence) {
+ switch (whence) {
+ case SEEK_SET: {
+ ptr = pos;
+ break;
+ }
+ case SEEK_CUR: {
+ ptr += pos;
+ break;
+ }
+ case SEEK_END: {
+ ptr = save.size - pos;
+ break;
+ }
+ }
+}
+
+bool DSSaveFile::eos() const {
+ return ptr >= (int) save.size;
+}
+
+void DSSaveFile::skip(uint32 bytes) {
+ ptr = ptr + bytes;
+ if (ptr > (int) save.size) ptr = save.size;
+}
+
+uint32 DSSaveFile::write(const void *buf, uint32 size) {
+
+ if (ptr + size > DS_MAX_SAVE_SIZE) {
+ size = DS_MAX_SAVE_SIZE - ptr;
+ }
+
+ memcpy(saveData + ptr, buf, size);
+ ptr += size;
+ save.size += size;
+ return size;
+}
+
+bool DSSaveFile::matches(char* prefix, int num) {
+ char str[16];
+ if (isValid()) {
+ sprintf(str, "%s%02d", prefix, num);
+ if (!strcmp(str, save.name)) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+bool DSSaveFile::matches(char* filename) {
+ if (isValid()) {
+ return !strcmp(save.name, filename);
+ } else {
+ return false;
+ }
+}
+
+void DSSaveFile::setName(char *name) {
+ save.isValid = true;
+ save.magic = 0xBEEFCAFE;
+ ownsData = true;
+ save.size = 0;
+ save.compressedSize = 0;
+ saveData = new unsigned char[DS_MAX_SAVE_SIZE];
+ strcpy(save.name, name);
+
+ if ((strstr(name, ".s99")) || (strstr(name, ".c"))) {
+ isTempFile = true;
+ } else {
+ isTempFile = false;
+ }
+}
+
+void DSSaveFile::clearData() {
+ save.size = 0;
+
+ if (saveCompressed) {
+ if (ownsData) {
+ delete saveData;
+ DSSaveFileManager::instance()->addBytesFree(getRamUsage());
+ }
+ saveData = new unsigned char[DS_MAX_SAVE_SIZE];
+ saveCompressed = false;
+ ownsData = true;
+ }
+
+}
+
+void DSSaveFile::deleteFile() {
+ if (isValid()) {
+ if (ownsData) {
+ DSSaveFileManager::instance()->addBytesFree(getRamUsage());
+ delete saveData;
+ saveData = NULL;
+ }
+ ptr = 0;
+ saveCompressed = false;
+ save.isValid = false;
+ ownsData = false;
+ isOpenFlag = true;
+ }
+}
+
+DSSaveFileManager::DSSaveFileManager() {
+ instancePtr = this;
+
+ *((u16 *) (0x4000204)) |= 0x3;
+ swiWaitForVBlank();
+
+ loadAllFromSRAM();
+}
+
+DSSaveFileManager::~DSSaveFileManager() {
+ instancePtr = NULL;
+}
+
+void DSSaveFileManager::loadAllFromSRAM() {
+ int addr = 1;
+
+ for (int r = 0; r < 8; r++) {
+ gbaSave[r].deleteFile();
+ }
+
+ sramBytesFree = 65533;
+
+ // Try to find saves in save RAM
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].loadFromSaveRAM(CART_RAM + addr)) {
+ addr += gbaSave[r].getRamUsage();
+ sramBytesFree -= gbaSave[r].getRamUsage();
+ }
+ }
+
+}
+
+void DSSaveFileManager::formatSram() {
+ for (int r = 0; r < 65533; r++) {
+ *(CART_RAM + r) = 0;
+ }
+
+ loadAllFromSRAM();
+}
+
+void DSSaveFileManager::listFiles() {
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid()) {
+ consolePrintf("'%s': %d bytes\n", gbaSave[r].getName(), gbaSave[r].getRamUsage());
+ }
+ }
+ consolePrintf("SRAM free: %d bytes\n", getBytesFree());
+}
+
+DSSaveFileManager* DSSaveFileManager::instancePtr = NULL;
+
+Common::SaveFile *DSSaveFileManager::openSavefile(const char* filename, bool saveOrLoad) {
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) filename))) {
+// consolePrintf("Matched save %d (%d)\n", r, gbaSave[r].getSize());
+ gbaSave[r].reset();
+ //consolePrintf("reset ");
+ if (saveOrLoad) gbaSave[r].clearData();
+// consolePrintf("cleared ");
+ return gbaSave[r].clone();
+ }
+ }
+
+ if (saveOrLoad) {
+ return makeSaveFile(filename, saveOrLoad);
+ } else {
+ return NULL;
+ }
+}
+
+
+
+DSSaveFile* DSSaveFile::clone() {
+// consolePrintf("Clone %s %d\n", save.name, save.size);
+ return new DSSaveFile(&save, saveCompressed, saveData);
+}
+
+void DSSaveFileManager::deleteFile(char* name) {
+// consolePrintf("Deleting %s", name);
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) name))) {
+ gbaSave[r].deleteFile();
+ }
+ }
+ flushToSaveRAM();
+}
+
+void DSSaveFileManager::listSavefiles(const char *prefix, bool *marks, int num) {
+ memset(marks, false, num*sizeof(bool));
+
+ for (int saveNum = 0; saveNum < num; saveNum++) {
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) prefix, saveNum))) {
+ marks[saveNum] = true;
+ }
+ }
+ }
+
+}
+
+Common::SaveFile *DSSaveFileManager::makeSaveFile(const char *filename, bool saveOrLoad) {
+
+ // Find a free save slot
+ int r = 0;
+
+ while ((r < 8) && (gbaSave[r].isValid())) {
+ r++;
+ }
+
+ if ((r == 8) && (gbaSave[r].isValid())) {
+ // No more saves
+ return NULL;
+ } else {
+ // Allocate this save
+// consolePrintf("Allocated save %d\n", r);
+ gbaSave[r].setName((char *) filename);
+ gbaSave[r].reset();
+ return gbaSave[r].clone();
+ }
+}
+
+void DSSaveFileManager::flushToSaveRAM() {
+ int cartAddr = 1;
+ int s;
+
+ *((u16 *) (0x4000204)) |= 0x3;
+
+ swiWaitForVBlank();
+
+ int size = 0;
+ for (int r = 0; (r < 8); r++) {
+ if (gbaSave[r].isValid()) {
+ gbaSave[r].compress();
+ if (!gbaSave[r].isTemp()) size += gbaSave[r].getRamUsage();
+ }
+ }
+
+ if (size <= 65533) {
+
+ for (int r = 0; r < 65533; r++) {
+ *(CART_RAM + r) = 0;
+ }
+
+ sramBytesFree = 65533;
+
+ for (int r = 0; (r < 8); r++) {
+ if (gbaSave[r].isValid() && (!gbaSave[r].isTemp())) {
+
+ cartAddr += s = gbaSave[r].saveToSaveRAM(CART_RAM + cartAddr);
+
+ /* if (s == 0) {
+ consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again.", gbaSave[r].getName());
+ failed = true;
+ }*/
+ }
+ }
+ } else {
+
+ consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again.");
+ loadAllFromSRAM();
+
+ }
+// consolePrintf("SRAM free: %d bytes\n", getBytesFree());
+}
+/* Ramsave
+ * Copyright (C) 2002-2004 Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+ // Save in order 1,2,3,4,larger 2,5
+#include "stdafx.h"
+#include "system.h"
+#include "ramsave.h"
+#include "nds.h"
+#include "compressor/lz.h"
+
+#define CART_RAM ((vu8 *) (0x0A000000))
+
+DSSaveFile::DSSaveFile() {
+ ptr = 0;
+ saveCompressed = false;
+ save.isValid = false;
+ ownsData = false;
+ isOpenFlag = true;
+ isTempFile = false;
+}
+
+DSSaveFile::DSSaveFile(SCUMMSave* s, bool compressed, u8* data) {
+ save = *s;
+ saveData = data;
+ ptr = 0;
+ saveCompressed = compressed;
+ isOpenFlag = true;
+
+ if (saveCompressed) {
+ u8* uncompressed = new unsigned char[save.size];
+ if (!uncompressed) consolePrintf("Out of memory allocating %d!\n", save.size);
+ LZ_Uncompress(saveData, uncompressed, save.compressedSize);
+ saveData = uncompressed;
+ ownsData = true;
+ saveCompressed = false;
+// consolePrintf("Decompressed. name=%s size=%d (%d)", save.name, save.size, save.compressedSize);
+
+ } else {
+ ownsData = false;
+ origHeader = s;
+ }
+
+ if (save.magic == (int) 0xBEEFCAFE) {
+ save.isValid = true;
+ } else {
+ save.isValid = false;
+ }
+
+ isTempFile = false;
+}
+
+DSSaveFile::~DSSaveFile() {
+ if (!ownsData) {
+ *origHeader = save;
+ DSSaveFileManager::instance()->flushToSaveRAM();
+ }
+ if (ownsData) {
+ delete saveData;
+ }
+}
+
+bool DSSaveFile::loadFromSaveRAM(vu8* address) {
+
+ SCUMMSave newSave;
+
+ for (int t = 0; t < (int) sizeof(newSave); t++) {
+ ((char *) (&newSave))[t] = *(address + t);
+ }
+
+
+ if (newSave.magic == 0xBEEFCAFE) {
+ newSave.isValid = true;
+
+ *((u16 *) (0x4000204)) |= 0x3;
+
+ saveData = new unsigned char[newSave.compressedSize];
+
+ for (int t = 0; t < (int) newSave.compressedSize; t++) {
+ ((char *) (saveData))[t] = *(address + t + sizeof(newSave));
+ }
+
+ if (ownsData) delete this->saveData;
+ save = newSave;
+ saveCompressed = true;
+ this->saveData = saveData;
+ ownsData = true;
+ ptr = 0;
+ return true;
+ }
+
+ return false;
+}
+
+void DSSaveFile::compress() {
+ if (!saveCompressed) {
+ unsigned char* compBuffer = new unsigned char[(save.size * 110) / 100];
+ int compSize = LZ_Compress((u8 *) saveData, compBuffer, save.size);
+ save.compressedSize = compSize;
+
+
+
+ delete saveData;
+
+ // Make the save smaller
+ saveData = (u8 *) realloc(compBuffer, save.compressedSize);
+ saveCompressed = true;
+ }
+}
+
+int DSSaveFile::saveToSaveRAM(vu8* address) {
+
+ unsigned char* compBuffer;
+ bool failed;
+
+
+ int compSize;
+
+ compress();
+
+ compSize = save.compressedSize;
+ compBuffer = saveData;
+
+ if (DSSaveFileManager::instance()->getBytesFree() >= getRamUsage()) {
+
+ DSSaveFileManager::instance()->addBytesFree(-getRamUsage());
+
+ // Write header
+ for (int t = 0; t < sizeof(save); t++) {
+ while (*(address + t) != ((char *) (&save))[t]) {
+ *(address + t) = ((char *) (&save))[t];
+ }
+ }
+
+ // Write compressed buffer
+ for (int t = sizeof(save); t < (int) sizeof(save) + compSize; t++) {
+ while (*(address + t) != compBuffer[t - sizeof(save)]) {
+ *(address + t) = compBuffer[t - sizeof(save)];
+ }
+ }
+
+ failed = false;
+ } else {
+ failed = true;
+ }
+
+
+ return failed? 0: compSize + sizeof(save);
+
+}
+
+void DSSaveFile::reset() {
+ ptr = 0;
+}
+
+uint32 DSSaveFile::read(void *buf, uint32 size) {
+ if (ptr + size > save.size) {
+ size = save.size - ptr;
+ if (size < 0) size = 0;
+ }
+ memcpy(buf, saveData + ptr, size);
+// consolePrintf("byte: %d ", ((u8 *) (buf))[0]);
+
+ ptr += size;
+ return size;
+}
+
+uint32 DSSaveFile::pos() const {
+ return ptr;
+}
+
+uint32 DSSaveFile::size() const {
+ return save.size;
+}
+
+void DSSaveFile::seek(int32 pos, int whence) {
+ switch (whence) {
+ case SEEK_SET: {
+ ptr = pos;
+ break;
+ }
+ case SEEK_CUR: {
+ ptr += pos;
+ break;
+ }
+ case SEEK_END: {
+ ptr = save.size - pos;
+ break;
+ }
+ }
+}
+
+bool DSSaveFile::eos() const {
+ return ptr >= (int) save.size;
+}
+
+void DSSaveFile::skip(uint32 bytes) {
+ ptr = ptr + bytes;
+ if (ptr > (int) save.size) ptr = save.size;
+}
+
+uint32 DSSaveFile::write(const void *buf, uint32 size) {
+
+ if (ptr + size > DS_MAX_SAVE_SIZE) {
+ size = DS_MAX_SAVE_SIZE - ptr;
+ }
+
+ memcpy(saveData + ptr, buf, size);
+ ptr += size;
+ save.size += size;
+ return size;
+}
+
+bool DSSaveFile::matches(char* prefix, int num) {
+ char str[16];
+ if (isValid()) {
+ sprintf(str, "%s%02d", prefix, num);
+ if (!strcmp(str, save.name)) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+bool DSSaveFile::matches(char* filename) {
+ if (isValid()) {
+ return !strcmp(save.name, filename);
+ } else {
+ return false;
+ }
+}
+
+void DSSaveFile::setName(char *name) {
+ save.isValid = true;
+ save.magic = 0xBEEFCAFE;
+ ownsData = true;
+ save.size = 0;
+ save.compressedSize = 0;
+ saveData = new unsigned char[DS_MAX_SAVE_SIZE];
+ strcpy(save.name, name);
+
+ if ((strstr(name, ".s99")) || (strstr(name, ".c"))) {
+ isTempFile = true;
+ } else {
+ isTempFile = false;
+ }
+}
+
+void DSSaveFile::clearData() {
+ save.size = 0;
+
+ if (saveCompressed) {
+ if (ownsData) {
+ delete saveData;
+ DSSaveFileManager::instance()->addBytesFree(getRamUsage());
+ }
+ saveData = new unsigned char[DS_MAX_SAVE_SIZE];
+ saveCompressed = false;
+ ownsData = true;
+ }
+
+}
+
+void DSSaveFile::deleteFile() {
+ if (isValid()) {
+ if (ownsData) {
+ DSSaveFileManager::instance()->addBytesFree(getRamUsage());
+ delete saveData;
+ saveData = NULL;
+ }
+ ptr = 0;
+ saveCompressed = false;
+ save.isValid = false;
+ ownsData = false;
+ isOpenFlag = true;
+ }
+}
+
+DSSaveFileManager::DSSaveFileManager() {
+ instancePtr = this;
+
+ *((u16 *) (0x4000204)) |= 0x3;
+ swiWaitForVBlank();
+
+ loadAllFromSRAM();
+}
+
+DSSaveFileManager::~DSSaveFileManager() {
+ instancePtr = NULL;
+}
+
+void DSSaveFileManager::loadAllFromSRAM() {
+ int addr = 1;
+
+ for (int r = 0; r < 8; r++) {
+ gbaSave[r].deleteFile();
+ }
+
+ sramBytesFree = 65533;
+
+ // Try to find saves in save RAM
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].loadFromSaveRAM(CART_RAM + addr)) {
+ addr += gbaSave[r].getRamUsage();
+ sramBytesFree -= gbaSave[r].getRamUsage();
+ }
+ }
+
+}
+
+void DSSaveFileManager::formatSram() {
+ for (int r = 0; r < 65533; r++) {
+ *(CART_RAM + r) = 0;
+ }
+
+ loadAllFromSRAM();
+}
+
+void DSSaveFileManager::listFiles() {
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid()) {
+ consolePrintf("'%s': %d bytes\n", gbaSave[r].getName(), gbaSave[r].getRamUsage());
+ }
+ }
+ consolePrintf("SRAM free: %d bytes\n", getBytesFree());
+}
+
+DSSaveFileManager* DSSaveFileManager::instancePtr = NULL;
+
+Common::SaveFile *DSSaveFileManager::openSavefile(const char* filename, bool saveOrLoad) {
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) filename))) {
+// consolePrintf("Matched save %d (%d)\n", r, gbaSave[r].getSize());
+ gbaSave[r].reset();
+ //consolePrintf("reset ");
+ if (saveOrLoad) gbaSave[r].clearData();
+// consolePrintf("cleared ");
+ return gbaSave[r].clone();
+ }
+ }
+
+ if (saveOrLoad) {
+ return makeSaveFile(filename, saveOrLoad);
+ } else {
+ return NULL;
+ }
+}
+
+
+
+DSSaveFile* DSSaveFile::clone() {
+// consolePrintf("Clone %s %d\n", save.name, save.size);
+ return new DSSaveFile(&save, saveCompressed, saveData);
+}
+
+void DSSaveFileManager::deleteFile(char* name) {
+// consolePrintf("Deleting %s", name);
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) name))) {
+ gbaSave[r].deleteFile();
+ }
+ }
+ flushToSaveRAM();
+}
+
+void DSSaveFileManager::listSavefiles(const char *prefix, bool *marks, int num) {
+ memset(marks, false, num*sizeof(bool));
+
+ for (int saveNum = 0; saveNum < num; saveNum++) {
+ for (int r = 0; r < 8; r++) {
+ if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) prefix, saveNum))) {
+ marks[saveNum] = true;
+ }
+ }
+ }
+
+}
+
+Common::SaveFile *DSSaveFileManager::makeSaveFile(const char *filename, bool saveOrLoad) {
+
+ // Find a free save slot
+ int r = 0;
+
+ while ((r < 8) && (gbaSave[r].isValid())) {
+ r++;
+ }
+
+ if ((r == 8) && (gbaSave[r].isValid())) {
+ // No more saves
+ return NULL;
+ } else {
+ // Allocate this save
+// consolePrintf("Allocated save %d\n", r);
+ gbaSave[r].setName((char *) filename);
+ gbaSave[r].reset();
+ return gbaSave[r].clone();
+ }
+}
+
+void DSSaveFileManager::flushToSaveRAM() {
+ int cartAddr = 1;
+ int s;
+
+ *((u16 *) (0x4000204)) |= 0x3;
+
+ swiWaitForVBlank();
+
+ int size = 0;
+ for (int r = 0; (r < 8); r++) {
+ if (gbaSave[r].isValid()) {
+ gbaSave[r].compress();
+ if (!gbaSave[r].isTemp()) size += gbaSave[r].getRamUsage();
+ }
+ }
+
+ if (size <= 65533) {
+
+ for (int r = 0; r < 65533; r++) {
+ *(CART_RAM + r) = 0;
+ }
+
+ sramBytesFree = 65533;
+
+ for (int r = 0; (r < 8); r++) {
+ if (gbaSave[r].isValid() && (!gbaSave[r].isTemp())) {
+
+ cartAddr += s = gbaSave[r].saveToSaveRAM(CART_RAM + cartAddr);
+
+ /* if (s == 0) {
+ consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again.", gbaSave[r].getName());
+ failed = true;
+ }*/
+ }
+ }
+ } else {
+
+ consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again.");
+ loadAllFromSRAM();
+
+ }
+// consolePrintf("SRAM free: %d bytes\n", getBytesFree());
+}
diff --git a/backends/platform/ds/arm9/source/ramsave.h b/backends/platform/ds/arm9/source/ramsave.h
new file mode 100644
index 0000000000..d7d029df78
--- /dev/null
+++ b/backends/platform/ds/arm9/source/ramsave.h
@@ -0,0 +1,280 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _RAMSAVE_H_
+#define _RAMSAVE_H_
+
+#include "system.h"
+#include "savefile.h"
+
+// SaveFileManager class
+
+#define DS_MAX_SAVE_SIZE 150000
+
+class DSSaveFile : public Common::SaveFile {
+ int address;
+ int ptr;
+ bool ownsData;
+ bool saveCompressed;
+
+ struct SCUMMSave {
+ u32 magic;
+ bool isValid;
+ bool pad;
+ char name[16];
+ u32 size;
+ u32 compressedSize;
+ u16 pad2;
+ u32 reserved;
+ } __attribute__ ((packed));
+
+ SCUMMSave save;
+ u8* saveData;
+ SCUMMSave* origHeader;
+ bool isOpenFlag;
+ bool isTempFile;
+
+public:
+ DSSaveFile();
+ DSSaveFile(SCUMMSave* s, bool saveCompressed, u8* data);
+ ~DSSaveFile();
+
+ void reset();
+
+ bool isOpen() const { return isOpenFlag; }
+ virtual bool eos() const;
+ virtual void skip(uint32 size);
+
+ virtual uint32 pos() const;
+ virtual uint32 size() const;
+ virtual void seek(int32 pos, int whence);
+
+ uint32 read(void *buf, uint32 size);
+ uint32 write(const void *buf, uint32 size);
+
+ void setName(char *name);
+ char* getName() { return save.name; }
+
+ bool isValid() { return save.isValid; }
+ bool isTemp() { return isTempFile; }
+ bool matches(char* prefix, int num);
+ bool matches(char* filename);
+
+ void clearData();
+ void compress();
+
+ int getRamUsage() { return sizeof(save) + save.compressedSize; }
+ char* getRamImage() { return (char *) &save; }
+
+ int getSize() { return save.size; }
+
+ DSSaveFile* clone();
+
+ bool loadFromSaveRAM(vu8* address);
+ int saveToSaveRAM(vu8* address);
+
+
+ void deleteFile();
+
+ void operator delete(void *p) {
+// consolePrintf("Finished! size=%d\n", ((DSSaveFile *) (p))->save->size);
+ }
+
+
+
+};
+
+
+
+class DSSaveFileManager : public Common::SaveFileManager {
+
+ DSSaveFile gbaSave[8];
+ static DSSaveFileManager* instancePtr;
+ int sramBytesFree;
+
+public:
+ DSSaveFileManager();
+ ~DSSaveFileManager();
+
+ static DSSaveFileManager* instance() { return instancePtr; }
+
+ Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad);
+
+ virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); }
+ virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); }
+
+
+ void listSavefiles(const char *prefix, bool *marks, int num);
+
+ void flushToSaveRAM();
+
+ void addBytesFree(int size) { sramBytesFree += size; }
+ int getBytesFree() { return sramBytesFree; }
+
+ void deleteFile(char* name);
+ void listFiles();
+ void formatSram();
+
+ void loadAllFromSRAM();
+
+protected:
+ Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad);
+};
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _RAMSAVE_H_
+#define _RAMSAVE_H_
+
+#include "system.h"
+#include "savefile.h"
+
+// SaveFileManager class
+
+#define DS_MAX_SAVE_SIZE 150000
+
+class DSSaveFile : public Common::SaveFile {
+ int address;
+ int ptr;
+ bool ownsData;
+ bool saveCompressed;
+
+ struct SCUMMSave {
+ u32 magic;
+ bool isValid;
+ bool pad;
+ char name[16];
+ u32 size;
+ u32 compressedSize;
+ u16 pad2;
+ u32 reserved;
+ } __attribute__ ((packed));
+
+ SCUMMSave save;
+ u8* saveData;
+ SCUMMSave* origHeader;
+ bool isOpenFlag;
+ bool isTempFile;
+
+public:
+ DSSaveFile();
+ DSSaveFile(SCUMMSave* s, bool saveCompressed, u8* data);
+ ~DSSaveFile();
+
+ void reset();
+
+ bool isOpen() const { return isOpenFlag; }
+ virtual bool eos() const;
+ virtual void skip(uint32 size);
+
+ virtual uint32 pos() const;
+ virtual uint32 size() const;
+ virtual void seek(int32 pos, int whence);
+
+ uint32 read(void *buf, uint32 size);
+ uint32 write(const void *buf, uint32 size);
+
+ void setName(char *name);
+ char* getName() { return save.name; }
+
+ bool isValid() { return save.isValid; }
+ bool isTemp() { return isTempFile; }
+ bool matches(char* prefix, int num);
+ bool matches(char* filename);
+
+ void clearData();
+ void compress();
+
+ int getRamUsage() { return sizeof(save) + save.compressedSize; }
+ char* getRamImage() { return (char *) &save; }
+
+ int getSize() { return save.size; }
+
+ DSSaveFile* clone();
+
+ bool loadFromSaveRAM(vu8* address);
+ int saveToSaveRAM(vu8* address);
+
+
+ void deleteFile();
+
+ void operator delete(void *p) {
+// consolePrintf("Finished! size=%d\n", ((DSSaveFile *) (p))->save->size);
+ }
+
+
+
+};
+
+
+
+class DSSaveFileManager : public Common::SaveFileManager {
+
+ DSSaveFile gbaSave[8];
+ static DSSaveFileManager* instancePtr;
+ int sramBytesFree;
+
+public:
+ DSSaveFileManager();
+ ~DSSaveFileManager();
+
+ static DSSaveFileManager* instance() { return instancePtr; }
+
+ Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad);
+
+ virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); }
+ virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); }
+
+
+ void listSavefiles(const char *prefix, bool *marks, int num);
+
+ void flushToSaveRAM();
+
+ void addBytesFree(int size) { sramBytesFree += size; }
+ int getBytesFree() { return sramBytesFree; }
+
+ void deleteFile(char* name);
+ void listFiles();
+ void formatSram();
+
+ void loadAllFromSRAM();
+
+protected:
+ Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad);
+};
+
+#endif
diff --git a/backends/platform/ds/arm9/source/scummconsole.c b/backends/platform/ds/arm9/source/scummconsole.c
new file mode 100644
index 0000000000..7efa43e24c
--- /dev/null
+++ b/backends/platform/ds/arm9/source/scummconsole.c
@@ -0,0 +1,1122 @@
+//////////////////////////////////////////////////////////////////////
+//
+// console.cpp -- provides basic print functionality
+//
+// version 0.1, February 14, 2005
+//
+// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto)
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any
+// damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any
+// purpose, including commercial applications, and to alter it and
+// redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you
+// must not claim that you wrote the original software. If you use
+// this software in a product, an acknowledgment in the product
+// documentation would be appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and
+// must not be misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+// Changelog:
+// 0.1: First version
+// 0.2: Fixed sprite mapping bug. 1D mapping should work now.
+// Changed some register defines for consistency.
+//
+//////////////////////////////////////////////////////////////////////
+
+
+#include <NDS.h>
+#include "scummconsole.h"
+
+#include <stdarg.h>
+
+#include <default_font_bin.h>
+
+/////////////////////////////////////////
+//global console variables
+
+#define CONSOLE_WIDTH 32
+#define CONSOLE_HEIGHT 24
+#define TAB_SIZE 3
+
+//map to print to
+u16* fontMap;
+
+//location of cursor
+u8 row, col;
+
+//font may not start on a character base boundry
+u16 fontOffset;
+
+//the first character in the set (0 if you have a full set)
+u16 fontStart;
+
+//the 16-color palette to use
+u16 fontPal;
+
+
+
+
+///////////////////////////////////////////////////////////
+//consoleInit
+// param:
+// font: 16 color font
+// charBase: the location the font data will be loaded to
+// numCharacters: count of characters in the font
+// charStart: The ascii number of the first character in the font set
+// if you have a full set this will be zero
+// map: pointer to the map you will be printing to.
+// pal: specifies the 16 color palette to use, if > 15 it will change all non-zero
+// entries in the font to use palette index 255
+void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth)
+{
+ int i;
+
+ row = col = 0;
+
+ fontStart = charStart;
+
+ fontOffset = 0;
+
+ fontMap = map;
+
+ if(bitDepth == 16)
+ {
+ if(pal < 16)
+ {
+ fontPal = pal << 12;
+
+ for (i = 0; i < numCharacters * 16; i++)
+ charBase[i] = font[i];
+ }
+ else
+ {
+ fontPal = 15 << 12;
+
+ for (i = 0; i < numCharacters * 16; i++)
+ {
+ u16 temp = 0;
+
+ if(font[i] & 0xF)
+ temp |= 0xF;
+ if(font[i] & 0xF0)
+ temp |= 0xF0;
+ if(font[i] & 0xF00)
+ temp |= 0xF00;
+ if(font[i] & 0xF000)
+ temp |= 0xF000;
+
+ charBase[i] = temp;
+ }
+ }
+ }//end if bitdepth
+ else
+ {
+ fontPal = 0;
+ for(i = 0; i < numCharacters * 16; i++)
+ {
+ u32 temp = 0;
+
+ if(font[i] & 0xF)
+ temp = 255;
+ if(font[i] & 0xF0)
+ temp |= 255 << 8;
+ if(font[i] & 0xF00)
+ temp |= 255 << 16;
+ if(font[i] & 0xF000)
+ temp |= 255 << 24;
+
+ ((u32*)charBase)[i] = temp;
+
+ }
+ }
+}
+
+void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth)
+{
+ consoleInit((u16 *) default_font_bin, charBase, 256, 0, map, CONSOLE_USE_COLOR255, bitDepth);
+}
+
+void consolePrintSet(int x, int y)
+{
+ if(y < CONSOLE_HEIGHT)
+ row = y;
+ else
+ row = CONSOLE_HEIGHT - 1;
+
+ if(x < CONSOLE_WIDTH)
+ col = x;
+ else
+ col = CONSOLE_WIDTH - 1;
+}
+
+void consolePrintChar(char c)
+{
+ int i;
+
+ if(col >= CONSOLE_WIDTH)
+ {
+ col = 0;
+
+ row++;
+ }
+
+ if(row >= CONSOLE_HEIGHT)
+ {
+ row--;
+
+ for(i = CONSOLE_WIDTH; i < CONSOLE_HEIGHT * CONSOLE_WIDTH; i++)
+ fontMap[i - CONSOLE_WIDTH] = fontMap[i];
+ for(i = 0; i < CONSOLE_WIDTH; i++)
+ fontMap[i + (CONSOLE_HEIGHT-1)*CONSOLE_WIDTH] = fontPal | (u16)(' ' + fontOffset - fontStart);
+
+
+ }
+
+ switch(c)
+ {
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ row++;
+ col = 0;
+ break;
+ case 9:
+ col += TAB_SIZE;
+ break;
+ default:
+ fontMap[col + row * CONSOLE_WIDTH] = fontPal | (u16)(c + fontOffset - fontStart);
+ col++;
+ break;
+
+ }
+
+
+}
+
+
+void printX(int w, unsigned d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[20] = {0};
+
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'A' - 10);
+ }
+ else if(i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void printx(int w, unsigned int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[20] = {0};
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'a' - 10);
+ }
+ else if(i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void printInt(int w, int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[20] = {0};
+
+ if(d < 0)
+ {
+ consolePrintChar('-');
+ d *= -1;
+ }
+
+ if (d == 0)
+ buf[loop++] = 0;
+ else while (d > 0)
+ {
+ buf[loop++] = d % 10;
+ d /= 10;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < loop)
+ consolePrintChar(buf[i] + '0');
+ else if(i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void printBin(int w, int d)
+{
+ int i;
+ int first = 0;
+ for (i = 31; i >= 0; i--)
+ {
+ if(d & BIT(i))
+ {
+ first = 1;
+ consolePrintChar('1');
+ }
+ else if (first || i == 0)
+ consolePrintChar('0');
+ else if (i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void print0X(int w, unsigned d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday
+
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < w || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'A' - 10);
+ }
+ }
+}
+
+void print0x(int w, unsigned int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday
+
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < w || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'a' - 10);
+ }
+ }
+}
+
+void print0Int(int w, int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday
+
+ if(d < 0)
+ {
+ consolePrintChar('-');
+ d *= -1;
+ }
+
+ while(d > 0)
+ {
+ buf[loop++] = d % 10;
+ d /= 10;
+ }
+
+ for (i = 15; i >= 0; i--)
+ if(buf[i] || i < w || i < loop)
+ consolePrintChar(buf[i] + '0');
+
+}
+
+void print0Bin(int w, int d)
+{
+ int i;
+ int first = 0;
+ for (i = 31; i >= 0; i--)
+ {
+ if(d & BIT(i))
+ {
+ first = 1;
+ consolePrintChar('1');
+ }
+ else if (first || i == 0)
+ consolePrintChar('0');
+ else if (i < w)
+ consolePrintChar('0');
+ }
+}
+
+void print(const char* s)
+{
+ for(; *s; s++) consolePrintChar(*s);
+}
+
+void printF(int w, float f)
+{
+ unsigned int* t = (unsigned int*)&f;
+ unsigned int fraction = (*t) & 0x007FFFFF;
+ int exp = ((*t) >> 23) & 0xFF;
+
+ if(*t & BIT(31))
+ consolePrintChar('-');
+
+
+ print0Bin(32, fraction);
+
+ printInt(1, fraction);
+ consolePrintChar('e');
+ printInt(1, exp - 127);
+
+ /*
+ if(exp == 0 && fraction == 0)
+ {
+ printInt(1,0);
+ }
+ else if(exp == 0xFF && fraction == 0)
+ {
+ print("Inifinite");
+ }
+ else
+ {
+ printInt(w,fraction);
+ consolePrintChar('e');
+ printInt(1,exp - 127);
+ }
+ */
+}
+
+void consolePrintf(const char* s, ...)
+{
+ int w = 1, z = 0;
+
+ va_list argp;
+
+ va_start(argp, s);
+
+
+ while(*s)
+ {
+ w = 1;
+ z = 0;
+
+ switch(*s)
+ {
+ case '%':
+ s++;
+ if(*s == '0')
+ {
+ z = 1;
+ s++;
+ }
+ if(*s > '0' && *s <= '9')
+ {
+ w = *s - '0';
+ s++;
+ }
+ switch (*s)
+ {
+ case 'i':
+ case 'I':
+ case 'd':
+ case 'D':
+ if(z)print0Int(w, va_arg(argp, int));
+ else printInt(w, va_arg(argp, int));
+ s++;
+ break;
+ case 'X':
+ if(z)print0X(w, va_arg(argp, int));
+ else printX(w, va_arg(argp, int));
+ s++;
+ break;
+
+ case 'x':
+ if(z)print0x(w, va_arg(argp, int));
+ else printx(w, va_arg(argp, int));
+ s++;
+ break;
+
+ case 'b':
+ case 'B':
+ if(z)print0Bin(w, va_arg(argp, int));
+ else printBin(w, va_arg(argp, int));
+ s++;
+ break;
+ case 'f':
+ case 'F':
+ printF(w,va_arg(argp, double));
+ s++;
+ break;
+ case 's':
+ case 'S':
+ print(va_arg(argp, char*));
+ s++;
+ break;
+ default:
+ consolePrintChar('%');
+ break;
+ }
+ default:
+ consolePrintChar(*s);
+ break;
+ }
+
+ s++;
+ }
+ va_end(argp);
+}
+
+void consolePutString(int x, int y, char* s)
+{
+ consolePrintSet(x, y);
+ consolePrintf(s);
+}
+
+void consolePutInt(int x, int y, int d)
+{
+ consolePrintSet(x,y);
+ printInt(1,d);
+}
+
+void consolePutX(int x, int y, int d)
+{
+ consolePrintSet(x, y);
+ printX(1,d);
+}
+
+void consolePutChar(int x, int y, char c)
+{
+ consolePrintSet(x, y);
+ consolePrintChar(c);
+}
+
+void consolePutBin(int x, int y, int b)
+{
+ consolePrintSet(x, y);
+ printBin(1,b);
+}
+void consoleClear(void)
+{
+ int i = 0;
+ consolePrintSet(0,0);
+
+ while(i++ < CONSOLE_HEIGHT * CONSOLE_WIDTH)
+ consolePrintChar(' ');
+
+ consolePrintSet(0,0);
+}
+//////////////////////////////////////////////////////////////////////
+//
+// console.cpp -- provides basic print functionality
+//
+// version 0.1, February 14, 2005
+//
+// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto)
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any
+// damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any
+// purpose, including commercial applications, and to alter it and
+// redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you
+// must not claim that you wrote the original software. If you use
+// this software in a product, an acknowledgment in the product
+// documentation would be appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and
+// must not be misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+// Changelog:
+// 0.1: First version
+// 0.2: Fixed sprite mapping bug. 1D mapping should work now.
+// Changed some register defines for consistency.
+//
+//////////////////////////////////////////////////////////////////////
+
+
+#include <NDS.h>
+#include "scummconsole.h"
+
+#include <stdarg.h>
+
+#include <default_font_bin.h>
+
+/////////////////////////////////////////
+//global console variables
+
+#define CONSOLE_WIDTH 32
+#define CONSOLE_HEIGHT 24
+#define TAB_SIZE 3
+
+//map to print to
+u16* fontMap;
+
+//location of cursor
+u8 row, col;
+
+//font may not start on a character base boundry
+u16 fontOffset;
+
+//the first character in the set (0 if you have a full set)
+u16 fontStart;
+
+//the 16-color palette to use
+u16 fontPal;
+
+
+
+
+///////////////////////////////////////////////////////////
+//consoleInit
+// param:
+// font: 16 color font
+// charBase: the location the font data will be loaded to
+// numCharacters: count of characters in the font
+// charStart: The ascii number of the first character in the font set
+// if you have a full set this will be zero
+// map: pointer to the map you will be printing to.
+// pal: specifies the 16 color palette to use, if > 15 it will change all non-zero
+// entries in the font to use palette index 255
+void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth)
+{
+ int i;
+
+ row = col = 0;
+
+ fontStart = charStart;
+
+ fontOffset = 0;
+
+ fontMap = map;
+
+ if(bitDepth == 16)
+ {
+ if(pal < 16)
+ {
+ fontPal = pal << 12;
+
+ for (i = 0; i < numCharacters * 16; i++)
+ charBase[i] = font[i];
+ }
+ else
+ {
+ fontPal = 15 << 12;
+
+ for (i = 0; i < numCharacters * 16; i++)
+ {
+ u16 temp = 0;
+
+ if(font[i] & 0xF)
+ temp |= 0xF;
+ if(font[i] & 0xF0)
+ temp |= 0xF0;
+ if(font[i] & 0xF00)
+ temp |= 0xF00;
+ if(font[i] & 0xF000)
+ temp |= 0xF000;
+
+ charBase[i] = temp;
+ }
+ }
+ }//end if bitdepth
+ else
+ {
+ fontPal = 0;
+ for(i = 0; i < numCharacters * 16; i++)
+ {
+ u32 temp = 0;
+
+ if(font[i] & 0xF)
+ temp = 255;
+ if(font[i] & 0xF0)
+ temp |= 255 << 8;
+ if(font[i] & 0xF00)
+ temp |= 255 << 16;
+ if(font[i] & 0xF000)
+ temp |= 255 << 24;
+
+ ((u32*)charBase)[i] = temp;
+
+ }
+ }
+}
+
+void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth)
+{
+ consoleInit((u16 *) default_font_bin, charBase, 256, 0, map, CONSOLE_USE_COLOR255, bitDepth);
+}
+
+void consolePrintSet(int x, int y)
+{
+ if(y < CONSOLE_HEIGHT)
+ row = y;
+ else
+ row = CONSOLE_HEIGHT - 1;
+
+ if(x < CONSOLE_WIDTH)
+ col = x;
+ else
+ col = CONSOLE_WIDTH - 1;
+}
+
+void consolePrintChar(char c)
+{
+ int i;
+
+ if(col >= CONSOLE_WIDTH)
+ {
+ col = 0;
+
+ row++;
+ }
+
+ if(row >= CONSOLE_HEIGHT)
+ {
+ row--;
+
+ for(i = CONSOLE_WIDTH; i < CONSOLE_HEIGHT * CONSOLE_WIDTH; i++)
+ fontMap[i - CONSOLE_WIDTH] = fontMap[i];
+ for(i = 0; i < CONSOLE_WIDTH; i++)
+ fontMap[i + (CONSOLE_HEIGHT-1)*CONSOLE_WIDTH] = fontPal | (u16)(' ' + fontOffset - fontStart);
+
+
+ }
+
+ switch(c)
+ {
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ row++;
+ col = 0;
+ break;
+ case 9:
+ col += TAB_SIZE;
+ break;
+ default:
+ fontMap[col + row * CONSOLE_WIDTH] = fontPal | (u16)(c + fontOffset - fontStart);
+ col++;
+ break;
+
+ }
+
+
+}
+
+
+void printX(int w, unsigned d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[20] = {0};
+
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'A' - 10);
+ }
+ else if(i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void printx(int w, unsigned int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[20] = {0};
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'a' - 10);
+ }
+ else if(i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void printInt(int w, int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[20] = {0};
+
+ if(d < 0)
+ {
+ consolePrintChar('-');
+ d *= -1;
+ }
+
+ if (d == 0)
+ buf[loop++] = 0;
+ else while (d > 0)
+ {
+ buf[loop++] = d % 10;
+ d /= 10;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < loop)
+ consolePrintChar(buf[i] + '0');
+ else if(i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void printBin(int w, int d)
+{
+ int i;
+ int first = 0;
+ for (i = 31; i >= 0; i--)
+ {
+ if(d & BIT(i))
+ {
+ first = 1;
+ consolePrintChar('1');
+ }
+ else if (first || i == 0)
+ consolePrintChar('0');
+ else if (i < w)
+ consolePrintChar(' ');
+ }
+}
+
+void print0X(int w, unsigned d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday
+
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < w || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'A' - 10);
+ }
+ }
+}
+
+void print0x(int w, unsigned int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday
+
+
+ while(d > 0)
+ {
+ buf[loop++] = d & 0xF;
+ d = d>>4;
+ }
+
+ for (i = 7; i >= 0; i--)
+ {
+ if(buf[i] || i < w || i < loop)
+ {
+ if(buf[i] < 10)
+ consolePrintChar(buf[i] + '0');
+ else
+ consolePrintChar(buf[i] + 'a' - 10);
+ }
+ }
+}
+
+void print0Int(int w, int d)
+{
+ int loop = 0;
+ int i = 0;
+
+ char buf[] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday
+
+ if(d < 0)
+ {
+ consolePrintChar('-');
+ d *= -1;
+ }
+
+ while(d > 0)
+ {
+ buf[loop++] = d % 10;
+ d /= 10;
+ }
+
+ for (i = 15; i >= 0; i--)
+ if(buf[i] || i < w || i < loop)
+ consolePrintChar(buf[i] + '0');
+
+}
+
+void print0Bin(int w, int d)
+{
+ int i;
+ int first = 0;
+ for (i = 31; i >= 0; i--)
+ {
+ if(d & BIT(i))
+ {
+ first = 1;
+ consolePrintChar('1');
+ }
+ else if (first || i == 0)
+ consolePrintChar('0');
+ else if (i < w)
+ consolePrintChar('0');
+ }
+}
+
+void print(const char* s)
+{
+ for(; *s; s++) consolePrintChar(*s);
+}
+
+void printF(int w, float f)
+{
+ unsigned int* t = (unsigned int*)&f;
+ unsigned int fraction = (*t) & 0x007FFFFF;
+ int exp = ((*t) >> 23) & 0xFF;
+
+ if(*t & BIT(31))
+ consolePrintChar('-');
+
+
+ print0Bin(32, fraction);
+
+ printInt(1, fraction);
+ consolePrintChar('e');
+ printInt(1, exp - 127);
+
+ /*
+ if(exp == 0 && fraction == 0)
+ {
+ printInt(1,0);
+ }
+ else if(exp == 0xFF && fraction == 0)
+ {
+ print("Inifinite");
+ }
+ else
+ {
+ printInt(w,fraction);
+ consolePrintChar('e');
+ printInt(1,exp - 127);
+ }
+ */
+}
+
+void consolePrintf(const char* s, ...)
+{
+ int w = 1, z = 0;
+
+ va_list argp;
+
+ va_start(argp, s);
+
+
+ while(*s)
+ {
+ w = 1;
+ z = 0;
+
+ switch(*s)
+ {
+ case '%':
+ s++;
+ if(*s == '0')
+ {
+ z = 1;
+ s++;
+ }
+ if(*s > '0' && *s <= '9')
+ {
+ w = *s - '0';
+ s++;
+ }
+ switch (*s)
+ {
+ case 'i':
+ case 'I':
+ case 'd':
+ case 'D':
+ if(z)print0Int(w, va_arg(argp, int));
+ else printInt(w, va_arg(argp, int));
+ s++;
+ break;
+ case 'X':
+ if(z)print0X(w, va_arg(argp, int));
+ else printX(w, va_arg(argp, int));
+ s++;
+ break;
+
+ case 'x':
+ if(z)print0x(w, va_arg(argp, int));
+ else printx(w, va_arg(argp, int));
+ s++;
+ break;
+
+ case 'b':
+ case 'B':
+ if(z)print0Bin(w, va_arg(argp, int));
+ else printBin(w, va_arg(argp, int));
+ s++;
+ break;
+ case 'f':
+ case 'F':
+ printF(w,va_arg(argp, double));
+ s++;
+ break;
+ case 's':
+ case 'S':
+ print(va_arg(argp, char*));
+ s++;
+ break;
+ default:
+ consolePrintChar('%');
+ break;
+ }
+ default:
+ consolePrintChar(*s);
+ break;
+ }
+
+ s++;
+ }
+ va_end(argp);
+}
+
+void consolePutString(int x, int y, char* s)
+{
+ consolePrintSet(x, y);
+ consolePrintf(s);
+}
+
+void consolePutInt(int x, int y, int d)
+{
+ consolePrintSet(x,y);
+ printInt(1,d);
+}
+
+void consolePutX(int x, int y, int d)
+{
+ consolePrintSet(x, y);
+ printX(1,d);
+}
+
+void consolePutChar(int x, int y, char c)
+{
+ consolePrintSet(x, y);
+ consolePrintChar(c);
+}
+
+void consolePutBin(int x, int y, int b)
+{
+ consolePrintSet(x, y);
+ printBin(1,b);
+}
+void consoleClear(void)
+{
+ int i = 0;
+ consolePrintSet(0,0);
+
+ while(i++ < CONSOLE_HEIGHT * CONSOLE_WIDTH)
+ consolePrintChar(' ');
+
+ consolePrintSet(0,0);
+}
diff --git a/backends/platform/ds/arm9/source/scummconsole.h b/backends/platform/ds/arm9/source/scummconsole.h
new file mode 100644
index 0000000000..6b9a677cc7
--- /dev/null
+++ b/backends/platform/ds/arm9/source/scummconsole.h
@@ -0,0 +1,124 @@
+//////////////////////////////////////////////////////////////////////
+//
+// consol.h --provides basic consol type print functionality
+//
+// version 0.1, February 14, 2005
+//
+// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto)
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any
+// damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any
+// purpose, including commercial applications, and to alter it and
+// redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you
+// must not claim that you wrote the original software. If you use
+// this software in a product, an acknowledgment in the product
+// documentation would be appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and
+// must not be misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+// Changelog:
+// 0.1: First version
+// 0.2: Fixed sprite mapping bug. 1D mapping should work now.
+// Changed some register defines for consistency.
+//
+//////////////////////////////////////////////////////////////////////
+#ifndef CONSOLE_H2
+#define CONSOLE_H2
+
+#define CONSOLE_USE_COLOR255 16
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth);
+void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth);
+
+void consolePrintf(const char* s, ...);
+
+void consolePrintSet(int x, int y);
+
+void consolePrintChar(char c);
+
+void consolePutString(int x, int y, char* s);
+void consolePutInt(int x, int y, int d);
+void consolePutX(int x, int y, int d);
+void consolePutChar(int x, int y, char c);
+void consolePutBin(int x, int y, int b);
+
+void consoleClear(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+//////////////////////////////////////////////////////////////////////
+//
+// consol.h --provides basic consol type print functionality
+//
+// version 0.1, February 14, 2005
+//
+// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto)
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any
+// damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any
+// purpose, including commercial applications, and to alter it and
+// redistribute it freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you
+// must not claim that you wrote the original software. If you use
+// this software in a product, an acknowledgment in the product
+// documentation would be appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and
+// must not be misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+// Changelog:
+// 0.1: First version
+// 0.2: Fixed sprite mapping bug. 1D mapping should work now.
+// Changed some register defines for consistency.
+//
+//////////////////////////////////////////////////////////////////////
+#ifndef CONSOLE_H2
+#define CONSOLE_H2
+
+#define CONSOLE_USE_COLOR255 16
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth);
+void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth);
+
+void consolePrintf(const char* s, ...);
+
+void consolePrintSet(int x, int y);
+
+void consolePrintChar(char c);
+
+void consolePutString(int x, int y, char* s);
+void consolePutInt(int x, int y, int d);
+void consolePutX(int x, int y, int d);
+void consolePutChar(int x, int y, char c);
+void consolePutBin(int x, int y, int b);
+
+void consoleClear(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/backends/platform/ds/arm9/source/scummhelp.cpp b/backends/platform/ds/arm9/source/scummhelp.cpp
new file mode 100644
index 0000000000..a76e1fa10f
--- /dev/null
+++ b/backends/platform/ds/arm9/source/scummhelp.cpp
@@ -0,0 +1,172 @@
+#include "stdafx.h"
+#include "scummhelp.h"
+
+#define ADD_BIND(k,d) do { key[i] = k; dsc[i] = d; i++; } while (0)
+#define ADD_TEXT(d) ADD_BIND("",d)
+#define ADD_LINE ADD_BIND("","")
+
+#define HELP_NUM_LINES 15
+
+namespace DS {
+
+void updateStrings(byte gameId, byte version, Common::Platform platform,
+ int page, Common::String &title, Common::String *&key, Common::String *&dsc) {
+ key = new Common::String[HELP_NUM_LINES];
+ dsc = new Common::String[HELP_NUM_LINES];
+ int i = 0;
+ switch (page) {
+ case 1: {
+ title = "DS Controls (right handed):";
+ ADD_BIND("Pad Left", "Left mouse button");
+ ADD_BIND("Pad Right", "Right mouse button");
+ ADD_BIND("Pad Up", "Mouse hover mode (no click)");
+ ADD_BIND("Pad Down", "Skip dialog line (some games)");
+ ADD_BIND("Start", "Pause/Game menu");
+ ADD_BIND("Select", "DS Options menu");
+ ADD_BIND("B", "Skip cutscenes");
+ ADD_BIND("A", "Switch screens");
+ ADD_BIND("Y", "Show/hide debug console");
+ ADD_BIND("X", "Show/hide keyboard");
+ ADD_BIND("L + Pad", "Scroll current touch screen view");
+ ADD_BIND("L + Pen", "Scroll current touch screen view");
+ ADD_BIND("L + B", "Zoom in");
+ ADD_BIND("L + A", "Zoom out");
+ break;
+ }
+
+ case 2: {
+ title = "DS Controls (left handed):";
+ ADD_BIND("Y", "Left mouse button");
+ ADD_BIND("A", "Right mouse button");
+ ADD_BIND("X", "Mouse hover mode (no click)");
+ ADD_BIND("B", "Skip dialog line (some games)");
+ ADD_BIND("Start", "Pause/Game menu");
+ ADD_BIND("Select", "DS Options menu");
+ ADD_BIND("Pad Down", "Skip cutscenes");
+ ADD_BIND("Pad Up", "Show/hide keyboard");
+ ADD_BIND("Pad Left", "Show/hide debug console");
+ ADD_BIND("Pad Right", "Swap screens");
+ ADD_BIND("R + Pad", "Scroll current touch screen view");
+ ADD_BIND("R + Pen", "Scroll current touch screen view");
+ ADD_BIND("R + B", "Zoom in");
+ ADD_BIND("R + A", "Zoom out");
+ break;
+ }
+
+ case 3: {
+ title = "Indiana Jones Fighting controls:";
+ ADD_BIND("Pad Left", "Move left");
+ ADD_BIND("Pad Right", "Move right");
+ ADD_BIND("Pad Up", "High guard");
+ ADD_BIND("Pad Down", "Guard down");
+ ADD_BIND("Y", "Guard middle");
+ ADD_BIND("X", "Punch high");
+ ADD_BIND("A", "Punch middle");
+ ADD_BIND("B", "Punch low");
+
+ ADD_BIND("L+R", "Hold during bootup to clear SRAM");
+ ADD_BIND("", "(flash cart only)");
+ break;
+ }
+ }
+
+
+ while (i < HELP_NUM_LINES) {
+ ADD_LINE;
+ }
+
+}
+
+}
+
+
+#undef ADD_BIND
+#undef ADD_TEXT
+#undef ADD_LINE
+
+#include "stdafx.h"
+#include "scummhelp.h"
+
+#define ADD_BIND(k,d) do { key[i] = k; dsc[i] = d; i++; } while (0)
+#define ADD_TEXT(d) ADD_BIND("",d)
+#define ADD_LINE ADD_BIND("","")
+
+#define HELP_NUM_LINES 15
+
+namespace DS {
+
+void updateStrings(byte gameId, byte version, Common::Platform platform,
+ int page, Common::String &title, Common::String *&key, Common::String *&dsc) {
+ key = new Common::String[HELP_NUM_LINES];
+ dsc = new Common::String[HELP_NUM_LINES];
+ int i = 0;
+ switch (page) {
+ case 1: {
+ title = "DS Controls (right handed):";
+ ADD_BIND("Pad Left", "Left mouse button");
+ ADD_BIND("Pad Right", "Right mouse button");
+ ADD_BIND("Pad Up", "Mouse hover mode (no click)");
+ ADD_BIND("Pad Down", "Skip dialog line (some games)");
+ ADD_BIND("Start", "Pause/Game menu");
+ ADD_BIND("Select", "DS Options menu");
+ ADD_BIND("B", "Skip cutscenes");
+ ADD_BIND("A", "Switch screens");
+ ADD_BIND("Y", "Show/hide debug console");
+ ADD_BIND("X", "Show/hide keyboard");
+ ADD_BIND("L + Pad", "Scroll current touch screen view");
+ ADD_BIND("L + Pen", "Scroll current touch screen view");
+ ADD_BIND("L + B", "Zoom in");
+ ADD_BIND("L + A", "Zoom out");
+ break;
+ }
+
+ case 2: {
+ title = "DS Controls (left handed):";
+ ADD_BIND("Y", "Left mouse button");
+ ADD_BIND("A", "Right mouse button");
+ ADD_BIND("X", "Mouse hover mode (no click)");
+ ADD_BIND("B", "Skip dialog line (some games)");
+ ADD_BIND("Start", "Pause/Game menu");
+ ADD_BIND("Select", "DS Options menu");
+ ADD_BIND("Pad Down", "Skip cutscenes");
+ ADD_BIND("Pad Up", "Show/hide keyboard");
+ ADD_BIND("Pad Left", "Show/hide debug console");
+ ADD_BIND("Pad Right", "Swap screens");
+ ADD_BIND("R + Pad", "Scroll current touch screen view");
+ ADD_BIND("R + Pen", "Scroll current touch screen view");
+ ADD_BIND("R + B", "Zoom in");
+ ADD_BIND("R + A", "Zoom out");
+ break;
+ }
+
+ case 3: {
+ title = "Indiana Jones Fighting controls:";
+ ADD_BIND("Pad Left", "Move left");
+ ADD_BIND("Pad Right", "Move right");
+ ADD_BIND("Pad Up", "High guard");
+ ADD_BIND("Pad Down", "Guard down");
+ ADD_BIND("Y", "Guard middle");
+ ADD_BIND("X", "Punch high");
+ ADD_BIND("A", "Punch middle");
+ ADD_BIND("B", "Punch low");
+
+ ADD_BIND("L+R", "Hold during bootup to clear SRAM");
+ ADD_BIND("", "(flash cart only)");
+ break;
+ }
+ }
+
+
+ while (i < HELP_NUM_LINES) {
+ ADD_LINE;
+ }
+
+}
+
+}
+
+
+#undef ADD_BIND
+#undef ADD_TEXT
+#undef ADD_LINE
+
diff --git a/backends/platform/ds/arm9/source/scummhelp.h b/backends/platform/ds/arm9/source/scummhelp.h
new file mode 100644
index 0000000000..ef7e27d251
--- /dev/null
+++ b/backends/platform/ds/arm9/source/scummhelp.h
@@ -0,0 +1,64 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _SCUMMHELP_H_
+#define _SCUMMHELP_H_
+
+#include "common/str.h"
+
+namespace DS {
+
+void updateStrings(byte gameId, byte version, Common::Platform platform,
+ int page, Common::String &title, Common::String *&key, Common::String *&dsc);
+
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _SCUMMHELP_H_
+#define _SCUMMHELP_H_
+
+#include "common/str.h"
+
+namespace DS {
+
+void updateStrings(byte gameId, byte version, Common::Platform platform,
+ int page, Common::String &title, Common::String *&key, Common::String *&dsc);
+
+}
+
+#endif
diff --git a/backends/platform/ds/arm9/source/touchkeyboard.cpp b/backends/platform/ds/arm9/source/touchkeyboard.cpp
new file mode 100644
index 0000000000..d3e8a61077
--- /dev/null
+++ b/backends/platform/ds/arm9/source/touchkeyboard.cpp
@@ -0,0 +1,626 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <NDS.h>
+#include "touchkeyboard.h"
+#include "keyboard_raw.h"
+#include "keyboard_pal_raw.h"
+#include "dsmain.h"
+#include "osystem_ds.h"
+
+namespace DS {
+
+struct key_data {
+ char keyNum;
+ char x, y;
+ int character;
+ bool pressed;
+};
+
+#define DS_NUM_KEYS 60
+#define DS_SHIFT 0
+#define DS_BACKSPACE 8
+#define DS_RETURN 13
+#define DS_CAPSLOCK 1
+
+
+key_data keys[DS_NUM_KEYS] = {
+ // Key number x y character
+
+ // Numbers
+ {28, 3, 0, '1'},
+ {29, 5, 0, '2'},
+ {30, 7, 0, '3'},
+ {31, 9, 0, '4'},
+ {32, 11, 0, '5'},
+ {33, 13, 0, '6'},
+ {34, 15, 0, '7'},
+ {35, 17, 0, '8'},
+ {36, 19, 0, '9'},
+ {27, 21, 0, '0'},
+ {45, 23, 0, SDLK_MINUS},
+ {50, 25, 0, SDLK_EQUALS},
+ {52, 27, 0, SDLK_BACKSPACE},
+
+ // Top row
+ {'Q'-'A' + 1, 4, 2, 'Q'},
+ {'W'-'A' + 1, 6, 2, 'W'},
+ {'E'-'A' + 1, 8, 2, 'E'},
+ {'R'-'A' + 1, 10, 2, 'R'},
+ {'T'-'A' + 1, 12, 2, 'T'},
+ {'Y'-'A' + 1, 14, 2, 'Y'},
+ {'U'-'A' + 1, 16, 2, 'U'},
+ {'I'-'A' + 1, 18, 2, 'I'},
+ {'O'-'A' + 1, 20, 2, 'O'},
+ {'P'-'A' + 1, 22, 2, 'P'},
+ {43, 24, 2, SDLK_LEFTBRACKET},
+ {44, 26, 2, SDLK_RIGHTBRACKET},
+
+ // Middle row
+ {55, 3, 4, DS_CAPSLOCK},
+ {'A'-'A' + 1, 5, 4, 'A'},
+ {'S'-'A' + 1, 7, 4, 'S'},
+ {'D'-'A' + 1, 9, 4, 'D'},
+ {'F'-'A' + 1, 11, 4, 'F'},
+ {'G'-'A' + 1, 13, 4, 'G'},
+ {'H'-'A' + 1, 15, 4, 'H'},
+ {'J'-'A' + 1, 17, 4, 'J'},
+ {'K'-'A' + 1, 19, 4, 'K'},
+ {'L'-'A' + 1, 21, 4, 'L'},
+ {42, 23, 4, SDLK_SEMICOLON},
+ {41, 25, 4, SDLK_QUOTE},
+ {46, 27, 4, SDLK_RETURN},
+
+ // Bottom row
+ {51, 4, 6, DS_SHIFT},
+ {'Z'-'A' + 1, 6, 6, 'Z'},
+ {'X'-'A' + 1, 8, 6, 'X'},
+ {'C'-'A' + 1, 10, 6, 'C'},
+ {'V'-'A' + 1, 12, 6, 'V'},
+ {'B'-'A' + 1, 14, 6, 'B'},
+ {'N'-'A' + 1, 16, 6, 'N'},
+ {'M'-'A' + 1, 18, 6, 'M'},
+ {38, 20, 6, SDLK_COMMA},
+ {39, 22, 6, SDLK_PERIOD},
+ {40, 24, 6, SDLK_SLASH},
+
+ // Space bar
+ {47, 9, 8, SDLK_SPACE},
+ {48, 11, 8, SDLK_SPACE},
+ {48, 13, 8, SDLK_SPACE},
+ {48, 15, 8, SDLK_SPACE},
+ {48, 17, 8, SDLK_SPACE},
+ {49, 19, 8, SDLK_SPACE},
+
+ // Cursor arrows
+ {52, 27, 8, SDLK_LEFT},
+ {54, 29, 8, SDLK_DOWN},
+ {53, 31, 8, SDLK_RIGHT},
+ {51, 29, 6, SDLK_UP},
+
+ // Close button
+ {56, 30, 0, SDLK_UNKNOWN},
+
+};
+
+int keyboardX;
+int keyboardY;
+
+int mapBase;
+int tileBase;
+
+bool shiftState;
+bool capsLockState;
+
+bool closed;
+
+void restoreVRAM(int tileBase, int mapBase, u16* saveSpace) {
+ for (int r = 0; r < 32 * 32; r++) {
+ ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = *saveSpace++;
+ }
+
+ for (int r = 0; r < 4096; r++) {
+ ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = *saveSpace++;
+ }
+}
+
+void drawKeyboard(int tileBase, int mapBase, u16* saveSpace) {
+
+
+ for (int r = 0; r < 32 * 32; r++) {
+// *saveSpace++ = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r];
+ ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = 127;
+ }
+
+ for (int r = 0; r < 4096; r++) {
+// *saveSpace++ = ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r];
+ ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = ((u16 *) (keyboard_raw))[r];
+ }
+
+ for (int r = 0; r < 16; r++) {
+ BG_PALETTE_SUB[r] = ((u16 *) (keyboard_pal_raw))[r];
+ }
+
+ for (int r = 0; r < 16; r++) {
+ int col = ((u16 *) (keyboard_pal_raw))[r];
+
+ int red = col & 0x001F;
+ int green = (col & 0x03E0) >> 5;
+ int blue = (col & 0x7C00) >> 10;
+
+ red = (red * 8) / 16;
+ green = (green * 8) / 16;
+ blue = (blue * 8) / 16;
+
+ BG_PALETTE_SUB[16 + r] = red | (green << 5) | (blue << 10);
+ }
+
+ keyboardX = -2;
+ keyboardY = 2;
+
+ mapBase = mapBase;
+ tileBase = tileBase;
+
+ shiftState = false;
+ capsLockState = false;
+
+ int x = keyboardX;
+ int y = keyboardY;
+
+ u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase));
+
+ for (int r = 0; r < DS_NUM_KEYS; r++) {
+ base[(y + keys[r].y) * 32 + x + keys[r].x] = keys[r].keyNum * 2;
+ base[(y + keys[r].y) * 32 + x + keys[r].x + 1] = keys[r].keyNum * 2 + 1;
+
+ base[(y + keys[r].y + 1) * 32 + x + keys[r].x] = 128 + keys[r].keyNum * 2;
+ base[(y + keys[r].y + 1) * 32 + x + keys[r].x + 1] = 128 + keys[r].keyNum * 2 + 1;
+
+ keys[r].pressed = false;
+ }
+
+ closed = false;
+}
+
+bool getKeyboardClosed() {
+ return closed;
+}
+
+void setKeyHighlight(int key, bool highlight) {
+ u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(DS::mapBase));
+
+ if (highlight) {
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] |= 0x1000;
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] |= 0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] |= 0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] |= 0x1000;
+ } else {
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] &= ~0x1000;
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] &= ~0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000;
+ }
+}
+
+void addKeyboardEvents() {
+ if (DS::getPenDown()) {
+ int x = IPC->touchXpx;
+ int y = IPC->touchYpx;
+
+ int tx = (x >> 3) - keyboardX;
+ int ty = (y >> 3) - keyboardY;
+
+// consolePrintf("x=%d y=%d\n", tx, ty);
+
+ for (int r = 0; r < DS_NUM_KEYS; r++) {
+ if (( (tx >= keys[r].x) && (tx <= keys[r].x + 1)) &&
+ (ty >= keys[r].y) && (ty <= keys[r].y + 1)) {
+ OSystem_DS* system = OSystem_DS::instance();
+ OSystem::Event event;
+
+// consolePrintf("Key: %d\n", r);
+ if ((keys[r].character == SDLK_UNKNOWN)) {
+ // Close button
+ DS::closed = true;
+ } else if ((keys[r].character >= '0') && (keys[r].character <= '9')) {
+ event.kbd.ascii = keys[r].character;
+ event.kbd.keycode = 0;
+
+ } else if ((keys[r].character >= 'A') && (keys[r].character <= 'Z')) {
+
+ if ((!DS::shiftState) && (!DS::capsLockState)) {
+ event.kbd.ascii = keys[r].character + 32; // Make key lowercase.
+ } else {
+ event.kbd.ascii = keys[r].character;
+ }
+
+ event.kbd.keycode = event.kbd.ascii;
+ } else {
+ event.kbd.ascii = keys[r].character;
+ event.kbd.keycode = keys[r].character;
+ }
+
+
+
+ //event.kbd.keycode = keys[r].character;
+ //event.kbd.ascii = keys[r].character;
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+
+ switch (keys[r].character) {
+ case DS_SHIFT: {
+ DS::shiftState = !DS::shiftState;
+ DS::setKeyHighlight(r, DS::shiftState);
+ break;
+ }
+
+ case DS_CAPSLOCK: {
+ DS::capsLockState = !DS::capsLockState;
+ DS::setKeyHighlight(r, DS::capsLockState);
+ break;
+ }
+
+ default: {
+ DS::setKeyHighlight(r, true);
+ keys[r].pressed = true;
+
+ if (DS::shiftState) {
+ DS::shiftState = false;
+ for (int t = 0; t < DS_NUM_KEYS; t++) {
+ if (keys[t].character == DS_SHIFT) {
+ DS::setKeyHighlight(t, false);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ }
+ }
+ }
+
+ if (DS::getPenReleased()) {
+ for (int r = 0; r < DS_NUM_KEYS; r++) {
+ if (keys[r].pressed) {
+ DS::setKeyHighlight(r, false);
+ keys[r].pressed = false;
+ }
+ }
+ }
+}
+
+}
+
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <NDS.h>
+#include "touchkeyboard.h"
+#include "keyboard_raw.h"
+#include "keyboard_pal_raw.h"
+#include "dsmain.h"
+#include "osystem_ds.h"
+
+namespace DS {
+
+struct key_data {
+ char keyNum;
+ char x, y;
+ int character;
+ bool pressed;
+};
+
+#define DS_NUM_KEYS 60
+#define DS_SHIFT 0
+#define DS_BACKSPACE 8
+#define DS_RETURN 13
+#define DS_CAPSLOCK 1
+
+
+key_data keys[DS_NUM_KEYS] = {
+ // Key number x y character
+
+ // Numbers
+ {28, 3, 0, '1'},
+ {29, 5, 0, '2'},
+ {30, 7, 0, '3'},
+ {31, 9, 0, '4'},
+ {32, 11, 0, '5'},
+ {33, 13, 0, '6'},
+ {34, 15, 0, '7'},
+ {35, 17, 0, '8'},
+ {36, 19, 0, '9'},
+ {27, 21, 0, '0'},
+ {45, 23, 0, SDLK_MINUS},
+ {50, 25, 0, SDLK_EQUALS},
+ {52, 27, 0, SDLK_BACKSPACE},
+
+ // Top row
+ {'Q'-'A' + 1, 4, 2, 'Q'},
+ {'W'-'A' + 1, 6, 2, 'W'},
+ {'E'-'A' + 1, 8, 2, 'E'},
+ {'R'-'A' + 1, 10, 2, 'R'},
+ {'T'-'A' + 1, 12, 2, 'T'},
+ {'Y'-'A' + 1, 14, 2, 'Y'},
+ {'U'-'A' + 1, 16, 2, 'U'},
+ {'I'-'A' + 1, 18, 2, 'I'},
+ {'O'-'A' + 1, 20, 2, 'O'},
+ {'P'-'A' + 1, 22, 2, 'P'},
+ {43, 24, 2, SDLK_LEFTBRACKET},
+ {44, 26, 2, SDLK_RIGHTBRACKET},
+
+ // Middle row
+ {55, 3, 4, DS_CAPSLOCK},
+ {'A'-'A' + 1, 5, 4, 'A'},
+ {'S'-'A' + 1, 7, 4, 'S'},
+ {'D'-'A' + 1, 9, 4, 'D'},
+ {'F'-'A' + 1, 11, 4, 'F'},
+ {'G'-'A' + 1, 13, 4, 'G'},
+ {'H'-'A' + 1, 15, 4, 'H'},
+ {'J'-'A' + 1, 17, 4, 'J'},
+ {'K'-'A' + 1, 19, 4, 'K'},
+ {'L'-'A' + 1, 21, 4, 'L'},
+ {42, 23, 4, SDLK_SEMICOLON},
+ {41, 25, 4, SDLK_QUOTE},
+ {46, 27, 4, SDLK_RETURN},
+
+ // Bottom row
+ {51, 4, 6, DS_SHIFT},
+ {'Z'-'A' + 1, 6, 6, 'Z'},
+ {'X'-'A' + 1, 8, 6, 'X'},
+ {'C'-'A' + 1, 10, 6, 'C'},
+ {'V'-'A' + 1, 12, 6, 'V'},
+ {'B'-'A' + 1, 14, 6, 'B'},
+ {'N'-'A' + 1, 16, 6, 'N'},
+ {'M'-'A' + 1, 18, 6, 'M'},
+ {38, 20, 6, SDLK_COMMA},
+ {39, 22, 6, SDLK_PERIOD},
+ {40, 24, 6, SDLK_SLASH},
+
+ // Space bar
+ {47, 9, 8, SDLK_SPACE},
+ {48, 11, 8, SDLK_SPACE},
+ {48, 13, 8, SDLK_SPACE},
+ {48, 15, 8, SDLK_SPACE},
+ {48, 17, 8, SDLK_SPACE},
+ {49, 19, 8, SDLK_SPACE},
+
+ // Cursor arrows
+ {52, 27, 8, SDLK_LEFT},
+ {54, 29, 8, SDLK_DOWN},
+ {53, 31, 8, SDLK_RIGHT},
+ {51, 29, 6, SDLK_UP},
+
+ // Close button
+ {56, 30, 0, SDLK_UNKNOWN},
+
+};
+
+int keyboardX;
+int keyboardY;
+
+int mapBase;
+int tileBase;
+
+bool shiftState;
+bool capsLockState;
+
+bool closed;
+
+void restoreVRAM(int tileBase, int mapBase, u16* saveSpace) {
+ for (int r = 0; r < 32 * 32; r++) {
+ ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = *saveSpace++;
+ }
+
+ for (int r = 0; r < 4096; r++) {
+ ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = *saveSpace++;
+ }
+}
+
+void drawKeyboard(int tileBase, int mapBase, u16* saveSpace) {
+
+
+ for (int r = 0; r < 32 * 32; r++) {
+// *saveSpace++ = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r];
+ ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = 127;
+ }
+
+ for (int r = 0; r < 4096; r++) {
+// *saveSpace++ = ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r];
+ ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = ((u16 *) (keyboard_raw))[r];
+ }
+
+ for (int r = 0; r < 16; r++) {
+ BG_PALETTE_SUB[r] = ((u16 *) (keyboard_pal_raw))[r];
+ }
+
+ for (int r = 0; r < 16; r++) {
+ int col = ((u16 *) (keyboard_pal_raw))[r];
+
+ int red = col & 0x001F;
+ int green = (col & 0x03E0) >> 5;
+ int blue = (col & 0x7C00) >> 10;
+
+ red = (red * 8) / 16;
+ green = (green * 8) / 16;
+ blue = (blue * 8) / 16;
+
+ BG_PALETTE_SUB[16 + r] = red | (green << 5) | (blue << 10);
+ }
+
+ keyboardX = -2;
+ keyboardY = 2;
+
+ mapBase = mapBase;
+ tileBase = tileBase;
+
+ shiftState = false;
+ capsLockState = false;
+
+ int x = keyboardX;
+ int y = keyboardY;
+
+ u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase));
+
+ for (int r = 0; r < DS_NUM_KEYS; r++) {
+ base[(y + keys[r].y) * 32 + x + keys[r].x] = keys[r].keyNum * 2;
+ base[(y + keys[r].y) * 32 + x + keys[r].x + 1] = keys[r].keyNum * 2 + 1;
+
+ base[(y + keys[r].y + 1) * 32 + x + keys[r].x] = 128 + keys[r].keyNum * 2;
+ base[(y + keys[r].y + 1) * 32 + x + keys[r].x + 1] = 128 + keys[r].keyNum * 2 + 1;
+
+ keys[r].pressed = false;
+ }
+
+ closed = false;
+}
+
+bool getKeyboardClosed() {
+ return closed;
+}
+
+void setKeyHighlight(int key, bool highlight) {
+ u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(DS::mapBase));
+
+ if (highlight) {
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] |= 0x1000;
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] |= 0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] |= 0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] |= 0x1000;
+ } else {
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] &= ~0x1000;
+ base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] &= ~0x1000;
+ base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000;
+ }
+}
+
+void addKeyboardEvents() {
+ if (DS::getPenDown()) {
+ int x = IPC->touchXpx;
+ int y = IPC->touchYpx;
+
+ int tx = (x >> 3) - keyboardX;
+ int ty = (y >> 3) - keyboardY;
+
+// consolePrintf("x=%d y=%d\n", tx, ty);
+
+ for (int r = 0; r < DS_NUM_KEYS; r++) {
+ if (( (tx >= keys[r].x) && (tx <= keys[r].x + 1)) &&
+ (ty >= keys[r].y) && (ty <= keys[r].y + 1)) {
+ OSystem_DS* system = OSystem_DS::instance();
+ OSystem::Event event;
+
+// consolePrintf("Key: %d\n", r);
+ if ((keys[r].character == SDLK_UNKNOWN)) {
+ // Close button
+ DS::closed = true;
+ } else if ((keys[r].character >= '0') && (keys[r].character <= '9')) {
+ event.kbd.ascii = keys[r].character;
+ event.kbd.keycode = 0;
+
+ } else if ((keys[r].character >= 'A') && (keys[r].character <= 'Z')) {
+
+ if ((!DS::shiftState) && (!DS::capsLockState)) {
+ event.kbd.ascii = keys[r].character + 32; // Make key lowercase.
+ } else {
+ event.kbd.ascii = keys[r].character;
+ }
+
+ event.kbd.keycode = event.kbd.ascii;
+ } else {
+ event.kbd.ascii = keys[r].character;
+ event.kbd.keycode = keys[r].character;
+ }
+
+
+
+ //event.kbd.keycode = keys[r].character;
+ //event.kbd.ascii = keys[r].character;
+ event.type = OSystem::EVENT_KEYDOWN;
+ event.kbd.flags = 0;
+ system->addEvent(event);
+
+ event.type = OSystem::EVENT_KEYUP;
+ system->addEvent(event);
+
+ switch (keys[r].character) {
+ case DS_SHIFT: {
+ DS::shiftState = !DS::shiftState;
+ DS::setKeyHighlight(r, DS::shiftState);
+ break;
+ }
+
+ case DS_CAPSLOCK: {
+ DS::capsLockState = !DS::capsLockState;
+ DS::setKeyHighlight(r, DS::capsLockState);
+ break;
+ }
+
+ default: {
+ DS::setKeyHighlight(r, true);
+ keys[r].pressed = true;
+
+ if (DS::shiftState) {
+ DS::shiftState = false;
+ for (int t = 0; t < DS_NUM_KEYS; t++) {
+ if (keys[t].character == DS_SHIFT) {
+ DS::setKeyHighlight(t, false);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ }
+ }
+ }
+
+ if (DS::getPenReleased()) {
+ for (int r = 0; r < DS_NUM_KEYS; r++) {
+ if (keys[r].pressed) {
+ DS::setKeyHighlight(r, false);
+ keys[r].pressed = false;
+ }
+ }
+ }
+}
+
+}
+
diff --git a/backends/platform/ds/arm9/source/touchkeyboard.h b/backends/platform/ds/arm9/source/touchkeyboard.h
new file mode 100644
index 0000000000..5afab98d03
--- /dev/null
+++ b/backends/platform/ds/arm9/source/touchkeyboard.h
@@ -0,0 +1,202 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _TOUCHKEYBOARD_H_
+#define _TOUCHKEYBOARD_H_
+
+namespace DS {
+
+enum SDLKey {
+ SDLK_UNKNOWN = 0, SDLK_FIRST = 0, SDLK_BACKSPACE = 8, SDLK_TAB = 9,
+ SDLK_CLEAR = 12, SDLK_RETURN = 13, SDLK_PAUSE = 19, SDLK_ESCAPE = 27,
+ SDLK_SPACE = 32, SDLK_EXCLAIM = 33, SDLK_QUOTEDBL = 34, SDLK_HASH = 35,
+ SDLK_DOLLAR = 36, SDLK_AMPERSAND = 38, SDLK_QUOTE = 39, SDLK_LEFTPAREN = 40,
+ SDLK_RIGHTPAREN = 41, SDLK_ASTERISK = 42, SDLK_PLUS = 43, SDLK_COMMA = 44,
+ SDLK_MINUS = 45, SDLK_PERIOD = 46, SDLK_SLASH = 47, SDLK_0 = 48,
+ SDLK_1 = 49, SDLK_2 = 50, SDLK_3 = 51, SDLK_4 = 52,
+ SDLK_5 = 53, SDLK_6 = 54, SDLK_7 = 55, SDLK_8 = 56,
+ SDLK_9 = 57, SDLK_COLON = 58, SDLK_SEMICOLON = 59, SDLK_LESS = 60,
+ SDLK_EQUALS = 61, SDLK_GREATER = 62, SDLK_QUESTION = 63, SDLK_AT = 64,
+ SDLK_LEFTBRACKET = 91, SDLK_BACKSLASH = 92, SDLK_RIGHTBRACKET = 93, SDLK_CARET = 94,
+ SDLK_UNDERSCORE = 95, SDLK_BACKQUOTE = 96, SDLK_a = 97, SDLK_b = 98,
+ SDLK_c = 99, SDLK_d = 100, SDLK_e = 101, SDLK_f = 102,
+ SDLK_g = 103, SDLK_h = 104, SDLK_i = 105, SDLK_j = 106,
+ SDLK_k = 107, SDLK_l = 108, SDLK_m = 109, SDLK_n = 110,
+ SDLK_o = 111, SDLK_p = 112, SDLK_q = 113, SDLK_r = 114,
+ SDLK_s = 115, SDLK_t = 116, SDLK_u = 117, SDLK_v = 118,
+ SDLK_w = 119, SDLK_x = 120, SDLK_y = 121, SDLK_z = 122,
+ SDLK_DELETE = 127, SDLK_WORLD_0 = 160, SDLK_WORLD_1 = 161, SDLK_WORLD_2 = 162,
+ SDLK_WORLD_3 = 163, SDLK_WORLD_4 = 164, SDLK_WORLD_5 = 165, SDLK_WORLD_6 = 166,
+ SDLK_WORLD_7 = 167, SDLK_WORLD_8 = 168, SDLK_WORLD_9 = 169, SDLK_WORLD_10 = 170,
+ SDLK_WORLD_11 = 171, SDLK_WORLD_12 = 172, SDLK_WORLD_13 = 173, SDLK_WORLD_14 = 174,
+ SDLK_WORLD_15 = 175, SDLK_WORLD_16 = 176, SDLK_WORLD_17 = 177, SDLK_WORLD_18 = 178,
+ SDLK_WORLD_19 = 179, SDLK_WORLD_20 = 180, SDLK_WORLD_21 = 181, SDLK_WORLD_22 = 182,
+ SDLK_WORLD_23 = 183, SDLK_WORLD_24 = 184, SDLK_WORLD_25 = 185, SDLK_WORLD_26 = 186,
+ SDLK_WORLD_27 = 187, SDLK_WORLD_28 = 188, SDLK_WORLD_29 = 189, SDLK_WORLD_30 = 190,
+ SDLK_WORLD_31 = 191, SDLK_WORLD_32 = 192, SDLK_WORLD_33 = 193, SDLK_WORLD_34 = 194,
+ SDLK_WORLD_35 = 195, SDLK_WORLD_36 = 196, SDLK_WORLD_37 = 197, SDLK_WORLD_38 = 198,
+ SDLK_WORLD_39 = 199, SDLK_WORLD_40 = 200, SDLK_WORLD_41 = 201, SDLK_WORLD_42 = 202,
+ SDLK_WORLD_43 = 203, SDLK_WORLD_44 = 204, SDLK_WORLD_45 = 205, SDLK_WORLD_46 = 206,
+ SDLK_WORLD_47 = 207, SDLK_WORLD_48 = 208, SDLK_WORLD_49 = 209, SDLK_WORLD_50 = 210,
+ SDLK_WORLD_51 = 211, SDLK_WORLD_52 = 212, SDLK_WORLD_53 = 213, SDLK_WORLD_54 = 214,
+ SDLK_WORLD_55 = 215, SDLK_WORLD_56 = 216, SDLK_WORLD_57 = 217, SDLK_WORLD_58 = 218,
+ SDLK_WORLD_59 = 219, SDLK_WORLD_60 = 220, SDLK_WORLD_61 = 221, SDLK_WORLD_62 = 222,
+ SDLK_WORLD_63 = 223, SDLK_WORLD_64 = 224, SDLK_WORLD_65 = 225, SDLK_WORLD_66 = 226,
+ SDLK_WORLD_67 = 227, SDLK_WORLD_68 = 228, SDLK_WORLD_69 = 229, SDLK_WORLD_70 = 230,
+ SDLK_WORLD_71 = 231, SDLK_WORLD_72 = 232, SDLK_WORLD_73 = 233, SDLK_WORLD_74 = 234,
+ SDLK_WORLD_75 = 235, SDLK_WORLD_76 = 236, SDLK_WORLD_77 = 237, SDLK_WORLD_78 = 238,
+ SDLK_WORLD_79 = 239, SDLK_WORLD_80 = 240, SDLK_WORLD_81 = 241, SDLK_WORLD_82 = 242,
+ SDLK_WORLD_83 = 243, SDLK_WORLD_84 = 244, SDLK_WORLD_85 = 245, SDLK_WORLD_86 = 246,
+ SDLK_WORLD_87 = 247, SDLK_WORLD_88 = 248, SDLK_WORLD_89 = 249, SDLK_WORLD_90 = 250,
+ SDLK_WORLD_91 = 251, SDLK_WORLD_92 = 252, SDLK_WORLD_93 = 253, SDLK_WORLD_94 = 254,
+ SDLK_WORLD_95 = 255, SDLK_KP0 = 256, SDLK_KP1 = 257, SDLK_KP2 = 258,
+ SDLK_KP3 = 259, SDLK_KP4 = 260, SDLK_KP5 = 261, SDLK_KP6 = 262,
+ SDLK_KP7 = 263, SDLK_KP8 = 264, SDLK_KP9 = 265, SDLK_KP_PERIOD = 266,
+ SDLK_KP_DIVIDE = 267, SDLK_KP_MULTIPLY = 268, SDLK_KP_MINUS = 269, SDLK_KP_PLUS = 270,
+ SDLK_KP_ENTER = 271, SDLK_KP_EQUALS = 272, SDLK_UP = 273, SDLK_DOWN = 274,
+ SDLK_RIGHT = 275, SDLK_LEFT = 276, SDLK_INSERT = 277, SDLK_HOME = 278,
+ SDLK_END = 279, SDLK_PAGEUP = 280, SDLK_PAGEDOWN = 281, SDLK_F1 = 282,
+ SDLK_F2 = 283, SDLK_F3 = 284, SDLK_F4 = 285, SDLK_F5 = 286,
+ SDLK_F6 = 287, SDLK_F7 = 288, SDLK_F8 = 289, SDLK_F9 = 290,
+ SDLK_F10 = 291, SDLK_F11 = 292, SDLK_F12 = 293, SDLK_F13 = 294,
+ SDLK_F14 = 295, SDLK_F15 = 296, SDLK_NUMLOCK = 300, SDLK_CAPSLOCK = 301,
+ SDLK_SCROLLOCK = 302, SDLK_RSHIFT = 303, SDLK_LSHIFT = 304, SDLK_RCTRL = 305,
+ SDLK_LCTRL = 306, SDLK_RALT = 307, SDLK_LALT = 308, SDLK_RMETA = 309,
+ SDLK_LMETA = 310, SDLK_LSUPER = 311, SDLK_RSUPER = 312, SDLK_MODE = 313,
+ SDLK_COMPOSE = 314, SDLK_HELP = 315, SDLK_PRINT = 316, SDLK_SYSREQ = 317,
+ SDLK_BREAK = 318, SDLK_MENU = 319, SDLK_POWER = 320, SDLK_EURO = 321,
+ SDLK_UNDO = 322, SDLK_LAST
+};
+
+enum SDLMod {
+ KMOD_NONE = 0x0000, KMOD_LSHIFT = 0x0001, KMOD_RSHIFT = 0x0002, KMOD_LCTRL = 0x0040,
+ KMOD_RCTRL = 0x0080, KMOD_LALT = 0x0100, KMOD_RALT = 0x0200, KMOD_LMETA = 0x0400,
+ KMOD_RMETA = 0x0800, KMOD_NUM = 0x1000, KMOD_CAPS = 0x2000, KMOD_MODE = 0x4000,
+ KMOD_RESERVED = 0x8000
+};
+
+void drawKeyboard(int tileBase, int mapBase, u16* saveSpace);
+void restoreVRAM(int tileBase, int mapBase, u16* saveSpace);
+void addKeyboardEvents();
+bool getKeyboardClosed();
+
+}
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _TOUCHKEYBOARD_H_
+#define _TOUCHKEYBOARD_H_
+
+namespace DS {
+
+enum SDLKey {
+ SDLK_UNKNOWN = 0, SDLK_FIRST = 0, SDLK_BACKSPACE = 8, SDLK_TAB = 9,
+ SDLK_CLEAR = 12, SDLK_RETURN = 13, SDLK_PAUSE = 19, SDLK_ESCAPE = 27,
+ SDLK_SPACE = 32, SDLK_EXCLAIM = 33, SDLK_QUOTEDBL = 34, SDLK_HASH = 35,
+ SDLK_DOLLAR = 36, SDLK_AMPERSAND = 38, SDLK_QUOTE = 39, SDLK_LEFTPAREN = 40,
+ SDLK_RIGHTPAREN = 41, SDLK_ASTERISK = 42, SDLK_PLUS = 43, SDLK_COMMA = 44,
+ SDLK_MINUS = 45, SDLK_PERIOD = 46, SDLK_SLASH = 47, SDLK_0 = 48,
+ SDLK_1 = 49, SDLK_2 = 50, SDLK_3 = 51, SDLK_4 = 52,
+ SDLK_5 = 53, SDLK_6 = 54, SDLK_7 = 55, SDLK_8 = 56,
+ SDLK_9 = 57, SDLK_COLON = 58, SDLK_SEMICOLON = 59, SDLK_LESS = 60,
+ SDLK_EQUALS = 61, SDLK_GREATER = 62, SDLK_QUESTION = 63, SDLK_AT = 64,
+ SDLK_LEFTBRACKET = 91, SDLK_BACKSLASH = 92, SDLK_RIGHTBRACKET = 93, SDLK_CARET = 94,
+ SDLK_UNDERSCORE = 95, SDLK_BACKQUOTE = 96, SDLK_a = 97, SDLK_b = 98,
+ SDLK_c = 99, SDLK_d = 100, SDLK_e = 101, SDLK_f = 102,
+ SDLK_g = 103, SDLK_h = 104, SDLK_i = 105, SDLK_j = 106,
+ SDLK_k = 107, SDLK_l = 108, SDLK_m = 109, SDLK_n = 110,
+ SDLK_o = 111, SDLK_p = 112, SDLK_q = 113, SDLK_r = 114,
+ SDLK_s = 115, SDLK_t = 116, SDLK_u = 117, SDLK_v = 118,
+ SDLK_w = 119, SDLK_x = 120, SDLK_y = 121, SDLK_z = 122,
+ SDLK_DELETE = 127, SDLK_WORLD_0 = 160, SDLK_WORLD_1 = 161, SDLK_WORLD_2 = 162,
+ SDLK_WORLD_3 = 163, SDLK_WORLD_4 = 164, SDLK_WORLD_5 = 165, SDLK_WORLD_6 = 166,
+ SDLK_WORLD_7 = 167, SDLK_WORLD_8 = 168, SDLK_WORLD_9 = 169, SDLK_WORLD_10 = 170,
+ SDLK_WORLD_11 = 171, SDLK_WORLD_12 = 172, SDLK_WORLD_13 = 173, SDLK_WORLD_14 = 174,
+ SDLK_WORLD_15 = 175, SDLK_WORLD_16 = 176, SDLK_WORLD_17 = 177, SDLK_WORLD_18 = 178,
+ SDLK_WORLD_19 = 179, SDLK_WORLD_20 = 180, SDLK_WORLD_21 = 181, SDLK_WORLD_22 = 182,
+ SDLK_WORLD_23 = 183, SDLK_WORLD_24 = 184, SDLK_WORLD_25 = 185, SDLK_WORLD_26 = 186,
+ SDLK_WORLD_27 = 187, SDLK_WORLD_28 = 188, SDLK_WORLD_29 = 189, SDLK_WORLD_30 = 190,
+ SDLK_WORLD_31 = 191, SDLK_WORLD_32 = 192, SDLK_WORLD_33 = 193, SDLK_WORLD_34 = 194,
+ SDLK_WORLD_35 = 195, SDLK_WORLD_36 = 196, SDLK_WORLD_37 = 197, SDLK_WORLD_38 = 198,
+ SDLK_WORLD_39 = 199, SDLK_WORLD_40 = 200, SDLK_WORLD_41 = 201, SDLK_WORLD_42 = 202,
+ SDLK_WORLD_43 = 203, SDLK_WORLD_44 = 204, SDLK_WORLD_45 = 205, SDLK_WORLD_46 = 206,
+ SDLK_WORLD_47 = 207, SDLK_WORLD_48 = 208, SDLK_WORLD_49 = 209, SDLK_WORLD_50 = 210,
+ SDLK_WORLD_51 = 211, SDLK_WORLD_52 = 212, SDLK_WORLD_53 = 213, SDLK_WORLD_54 = 214,
+ SDLK_WORLD_55 = 215, SDLK_WORLD_56 = 216, SDLK_WORLD_57 = 217, SDLK_WORLD_58 = 218,
+ SDLK_WORLD_59 = 219, SDLK_WORLD_60 = 220, SDLK_WORLD_61 = 221, SDLK_WORLD_62 = 222,
+ SDLK_WORLD_63 = 223, SDLK_WORLD_64 = 224, SDLK_WORLD_65 = 225, SDLK_WORLD_66 = 226,
+ SDLK_WORLD_67 = 227, SDLK_WORLD_68 = 228, SDLK_WORLD_69 = 229, SDLK_WORLD_70 = 230,
+ SDLK_WORLD_71 = 231, SDLK_WORLD_72 = 232, SDLK_WORLD_73 = 233, SDLK_WORLD_74 = 234,
+ SDLK_WORLD_75 = 235, SDLK_WORLD_76 = 236, SDLK_WORLD_77 = 237, SDLK_WORLD_78 = 238,
+ SDLK_WORLD_79 = 239, SDLK_WORLD_80 = 240, SDLK_WORLD_81 = 241, SDLK_WORLD_82 = 242,
+ SDLK_WORLD_83 = 243, SDLK_WORLD_84 = 244, SDLK_WORLD_85 = 245, SDLK_WORLD_86 = 246,
+ SDLK_WORLD_87 = 247, SDLK_WORLD_88 = 248, SDLK_WORLD_89 = 249, SDLK_WORLD_90 = 250,
+ SDLK_WORLD_91 = 251, SDLK_WORLD_92 = 252, SDLK_WORLD_93 = 253, SDLK_WORLD_94 = 254,
+ SDLK_WORLD_95 = 255, SDLK_KP0 = 256, SDLK_KP1 = 257, SDLK_KP2 = 258,
+ SDLK_KP3 = 259, SDLK_KP4 = 260, SDLK_KP5 = 261, SDLK_KP6 = 262,
+ SDLK_KP7 = 263, SDLK_KP8 = 264, SDLK_KP9 = 265, SDLK_KP_PERIOD = 266,
+ SDLK_KP_DIVIDE = 267, SDLK_KP_MULTIPLY = 268, SDLK_KP_MINUS = 269, SDLK_KP_PLUS = 270,
+ SDLK_KP_ENTER = 271, SDLK_KP_EQUALS = 272, SDLK_UP = 273, SDLK_DOWN = 274,
+ SDLK_RIGHT = 275, SDLK_LEFT = 276, SDLK_INSERT = 277, SDLK_HOME = 278,
+ SDLK_END = 279, SDLK_PAGEUP = 280, SDLK_PAGEDOWN = 281, SDLK_F1 = 282,
+ SDLK_F2 = 283, SDLK_F3 = 284, SDLK_F4 = 285, SDLK_F5 = 286,
+ SDLK_F6 = 287, SDLK_F7 = 288, SDLK_F8 = 289, SDLK_F9 = 290,
+ SDLK_F10 = 291, SDLK_F11 = 292, SDLK_F12 = 293, SDLK_F13 = 294,
+ SDLK_F14 = 295, SDLK_F15 = 296, SDLK_NUMLOCK = 300, SDLK_CAPSLOCK = 301,
+ SDLK_SCROLLOCK = 302, SDLK_RSHIFT = 303, SDLK_LSHIFT = 304, SDLK_RCTRL = 305,
+ SDLK_LCTRL = 306, SDLK_RALT = 307, SDLK_LALT = 308, SDLK_RMETA = 309,
+ SDLK_LMETA = 310, SDLK_LSUPER = 311, SDLK_RSUPER = 312, SDLK_MODE = 313,
+ SDLK_COMPOSE = 314, SDLK_HELP = 315, SDLK_PRINT = 316, SDLK_SYSREQ = 317,
+ SDLK_BREAK = 318, SDLK_MENU = 319, SDLK_POWER = 320, SDLK_EURO = 321,
+ SDLK_UNDO = 322, SDLK_LAST
+};
+
+enum SDLMod {
+ KMOD_NONE = 0x0000, KMOD_LSHIFT = 0x0001, KMOD_RSHIFT = 0x0002, KMOD_LCTRL = 0x0040,
+ KMOD_RCTRL = 0x0080, KMOD_LALT = 0x0100, KMOD_RALT = 0x0200, KMOD_LMETA = 0x0400,
+ KMOD_RMETA = 0x0800, KMOD_NUM = 0x1000, KMOD_CAPS = 0x2000, KMOD_MODE = 0x4000,
+ KMOD_RESERVED = 0x8000
+};
+
+void drawKeyboard(int tileBase, int mapBase, u16* saveSpace);
+void restoreVRAM(int tileBase, int mapBase, u16* saveSpace);
+void addKeyboardEvents();
+bool getKeyboardClosed();
+
+}
+
+#endif
diff --git a/backends/platform/ds/arm9/source/zipreader.cpp b/backends/platform/ds/arm9/source/zipreader.cpp
new file mode 100644
index 0000000000..b8780913ec
--- /dev/null
+++ b/backends/platform/ds/arm9/source/zipreader.cpp
@@ -0,0 +1,442 @@
+/* Zip File Reader
+ * Copyright (C) 2002-2004 Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include "stdafx.h"
+#include "zipreader.h"
+
+#define SWAP_U16(v) (((v & 0xFF) << 8) + (v & 0xFF00))
+#define SWAP_U32(v) ((SWAP_U16((v & 0XFFFF0000) >> 16) + (SWAP_U16(v & 0x0000FFFF) << 16)))
+
+
+ZipFile::ZipFile() {
+ // Locate a zip file in cartridge memory space
+
+ consolePrintf("ZIP file check...");
+
+ char* p = (char *) ZF_SEARCH_START;
+ bool found = false;
+
+ _zipFile = NULL;
+
+ while ((p != (char *) ZF_SEARCH_END) && (!found)) {
+ // Zip file header is: 0x504B0304
+
+ if ( (*p == 0x50) && (*(p + 1) == 0x4B) && (*(p + 2) == 0x03) && (*(p + 3) == 0x04) ) {
+ // Found header!
+ found = true;
+ _zipFile = p;
+ }
+
+ if (!found) p += ZF_SEARCH_STRIDE;
+
+ }
+
+ if (_zipFile) {
+ consolePrintf("Ok!\n");
+ } else {
+ consolePrintf("Not in use!\n");
+ return;
+ }
+
+ changeToRoot();
+ restartFile();
+
+ if (_currentFile->compSize != (u32) getFileSize()) {
+ consolePrintf("Error: ZIP file contains compression!\n");
+ }
+
+ _allFilesVisible = false;
+}
+
+bool ZipFile::isReady() {
+ return _zipFile != NULL;
+}
+
+bool ZipFile::restartFile() {
+ _currentFile = (FileHeader *) _zipFile;
+ char name[128];
+ getFileName(name);
+
+ bool more = true;
+
+ while (!currentFileInFolder() && more) {
+ char name[128];
+ getFileName(name);
+ more = skipFile();
+ }
+
+ return more;
+}
+
+bool ZipFile::currentFileInFolder() {
+ char name[128];
+
+ if (_allFilesVisible) return true;
+
+ getFileName(name);
+
+ if (_directory[0] == 0) { // Root directory
+ name[strlen(name) - 1] = 0;
+ return !strchr(name, '\\'); // Not in root if contains a / character before the last character
+ } else {
+/* if (name starts with directory && it's not the directory
+ && (no slashes after the directory || it's the last character)
+ && (slash follows directory)
+ */
+ if ((strstr(name, _directory) == name) && (strlen(name) != strlen(_directory))
+ && ((strchr(name + strlen(_directory) + 1, '\\') == NULL)
+ || (strchr(name + strlen(_directory) + 1, '\\') == name + strlen(name) - 1))
+ && (*(name + strlen(_directory)) == '\\')) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ZipFile::getFileName(char* name) {
+ strncpy(name, (char *) (_currentFile + 1), _currentFile->nameLength);
+
+ for (int r = 0; r < (int) strlen(name); r++) {
+ if (name[r] == '/') name[r] = '\\';
+ }
+
+ name[_currentFile->nameLength] = 0;
+
+ if (name[strlen(name) - 1] == '\\') {
+ name[strlen(name) - 1] = 0;
+ }
+}
+
+bool ZipFile::skipFile() {
+ bool valid;
+
+ do {
+
+ // Move on to the next file
+ _currentFile = (FileHeader *) (
+ ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->fileSize + _currentFile->extraLength
+ );
+
+ // Return true if there are more files. Check this by looking for the magic number
+ valid = (_currentFile->magic[0] == 0x50) &&
+ (_currentFile->magic[1] == 0x4B) &&
+ (_currentFile->magic[2] == 0x03) &&
+ (_currentFile->magic[3] == 0x04);
+
+
+ } while (valid && !currentFileInFolder());
+
+ return valid;
+
+ // Currently doesn't handle data descriptors!
+}
+
+
+
+u32 ZipFile::misaligned32(u32* v) {
+ char* b = (char *) v;
+ return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
+}
+
+u16 ZipFile::misaligned16(u16* v) {
+ char* b = (char *) v;
+ return (b[0] << 8) + b[1];
+}
+
+int ZipFile::getFileSize() {
+ return _currentFile->fileSize;
+}
+
+bool ZipFile::isDirectory() {
+ return _currentFile->fileSize == 0; // This is a bit wrong, but seems to work.
+}
+
+char* ZipFile::getFile() {
+ return ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->extraLength;
+}
+
+bool ZipFile::findFile(char* search) {
+ changeToRoot();
+ restartFile();
+
+ char searchName[128];
+ strcpy(searchName, search);
+ for (int r = 0; r < (int) strlen(searchName); r++) {
+ if (searchName[r] == '/') searchName[r] = '\\';
+ }
+
+ if (*(searchName + strlen(searchName) - 1) == '\\') { // Directories have a terminating slash
+ *(searchName + strlen(searchName) - 1) = '\0'; // which we need to dispose of.
+ }
+
+
+ do {
+ char name[128];
+ getFileName(name);
+ if (*(name + strlen(name) - 1) == '\\') { // Directories have a terminating slash
+ *(name + strlen(name) - 1) = '\0'; // which we need to dispose of.
+ }
+
+
+ if (!stricmp(name, searchName)) {
+// consolePrintf("'%s'=='%s'\n", name, searchName);
+ return true; // Got it!
+ } else {
+// consolePrintf("'%s'!='%s'\n", name, searchName);
+ }
+ } while (skipFile());
+
+ return false; // File wasn't found
+}
+
+void ZipFile::changeToRoot() {
+ _directory[0] = 0;
+}
+
+void ZipFile::changeDirectory(char* dir) {
+// consolePrintf("Current dir now '%s'\n", dir);
+ strcpy(_directory, dir);
+}
+
+ZipFile::~ZipFile() {
+
+}
+/* Zip File Reader
+ * Copyright (C) 2002-2004 Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include "stdafx.h"
+#include "zipreader.h"
+
+#define SWAP_U16(v) (((v & 0xFF) << 8) + (v & 0xFF00))
+#define SWAP_U32(v) ((SWAP_U16((v & 0XFFFF0000) >> 16) + (SWAP_U16(v & 0x0000FFFF) << 16)))
+
+
+ZipFile::ZipFile() {
+ // Locate a zip file in cartridge memory space
+
+ consolePrintf("ZIP file check...");
+
+ char* p = (char *) ZF_SEARCH_START;
+ bool found = false;
+
+ _zipFile = NULL;
+
+ while ((p != (char *) ZF_SEARCH_END) && (!found)) {
+ // Zip file header is: 0x504B0304
+
+ if ( (*p == 0x50) && (*(p + 1) == 0x4B) && (*(p + 2) == 0x03) && (*(p + 3) == 0x04) ) {
+ // Found header!
+ found = true;
+ _zipFile = p;
+ }
+
+ if (!found) p += ZF_SEARCH_STRIDE;
+
+ }
+
+ if (_zipFile) {
+ consolePrintf("Ok!\n");
+ } else {
+ consolePrintf("Not in use!\n");
+ return;
+ }
+
+ changeToRoot();
+ restartFile();
+
+ if (_currentFile->compSize != (u32) getFileSize()) {
+ consolePrintf("Error: ZIP file contains compression!\n");
+ }
+
+ _allFilesVisible = false;
+}
+
+bool ZipFile::isReady() {
+ return _zipFile != NULL;
+}
+
+bool ZipFile::restartFile() {
+ _currentFile = (FileHeader *) _zipFile;
+ char name[128];
+ getFileName(name);
+
+ bool more = true;
+
+ while (!currentFileInFolder() && more) {
+ char name[128];
+ getFileName(name);
+ more = skipFile();
+ }
+
+ return more;
+}
+
+bool ZipFile::currentFileInFolder() {
+ char name[128];
+
+ if (_allFilesVisible) return true;
+
+ getFileName(name);
+
+ if (_directory[0] == 0) { // Root directory
+ name[strlen(name) - 1] = 0;
+ return !strchr(name, '\\'); // Not in root if contains a / character before the last character
+ } else {
+/* if (name starts with directory && it's not the directory
+ && (no slashes after the directory || it's the last character)
+ && (slash follows directory)
+ */
+ if ((strstr(name, _directory) == name) && (strlen(name) != strlen(_directory))
+ && ((strchr(name + strlen(_directory) + 1, '\\') == NULL)
+ || (strchr(name + strlen(_directory) + 1, '\\') == name + strlen(name) - 1))
+ && (*(name + strlen(_directory)) == '\\')) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ZipFile::getFileName(char* name) {
+ strncpy(name, (char *) (_currentFile + 1), _currentFile->nameLength);
+
+ for (int r = 0; r < (int) strlen(name); r++) {
+ if (name[r] == '/') name[r] = '\\';
+ }
+
+ name[_currentFile->nameLength] = 0;
+
+ if (name[strlen(name) - 1] == '\\') {
+ name[strlen(name) - 1] = 0;
+ }
+}
+
+bool ZipFile::skipFile() {
+ bool valid;
+
+ do {
+
+ // Move on to the next file
+ _currentFile = (FileHeader *) (
+ ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->fileSize + _currentFile->extraLength
+ );
+
+ // Return true if there are more files. Check this by looking for the magic number
+ valid = (_currentFile->magic[0] == 0x50) &&
+ (_currentFile->magic[1] == 0x4B) &&
+ (_currentFile->magic[2] == 0x03) &&
+ (_currentFile->magic[3] == 0x04);
+
+
+ } while (valid && !currentFileInFolder());
+
+ return valid;
+
+ // Currently doesn't handle data descriptors!
+}
+
+
+
+u32 ZipFile::misaligned32(u32* v) {
+ char* b = (char *) v;
+ return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
+}
+
+u16 ZipFile::misaligned16(u16* v) {
+ char* b = (char *) v;
+ return (b[0] << 8) + b[1];
+}
+
+int ZipFile::getFileSize() {
+ return _currentFile->fileSize;
+}
+
+bool ZipFile::isDirectory() {
+ return _currentFile->fileSize == 0; // This is a bit wrong, but seems to work.
+}
+
+char* ZipFile::getFile() {
+ return ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->extraLength;
+}
+
+bool ZipFile::findFile(char* search) {
+ changeToRoot();
+ restartFile();
+
+ char searchName[128];
+ strcpy(searchName, search);
+ for (int r = 0; r < (int) strlen(searchName); r++) {
+ if (searchName[r] == '/') searchName[r] = '\\';
+ }
+
+ if (*(searchName + strlen(searchName) - 1) == '\\') { // Directories have a terminating slash
+ *(searchName + strlen(searchName) - 1) = '\0'; // which we need to dispose of.
+ }
+
+
+ do {
+ char name[128];
+ getFileName(name);
+ if (*(name + strlen(name) - 1) == '\\') { // Directories have a terminating slash
+ *(name + strlen(name) - 1) = '\0'; // which we need to dispose of.
+ }
+
+
+ if (!stricmp(name, searchName)) {
+// consolePrintf("'%s'=='%s'\n", name, searchName);
+ return true; // Got it!
+ } else {
+// consolePrintf("'%s'!='%s'\n", name, searchName);
+ }
+ } while (skipFile());
+
+ return false; // File wasn't found
+}
+
+void ZipFile::changeToRoot() {
+ _directory[0] = 0;
+}
+
+void ZipFile::changeDirectory(char* dir) {
+// consolePrintf("Current dir now '%s'\n", dir);
+ strcpy(_directory, dir);
+}
+
+ZipFile::~ZipFile() {
+
+}
diff --git a/backends/platform/ds/arm9/source/zipreader.h b/backends/platform/ds/arm9/source/zipreader.h
new file mode 100644
index 0000000000..4b01735111
--- /dev/null
+++ b/backends/platform/ds/arm9/source/zipreader.h
@@ -0,0 +1,162 @@
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _ZIPREADER_H_
+#define _ZIPREADER_H_
+#include "stdafx.h"
+#include "portdefs.h"
+#define ZF_SEARCH_START 0x08000000
+#define ZF_SEARCH_END 0x09000000
+#define ZF_SEARCH_STRIDE 16
+
+class ZipFile {
+
+ struct FileHeader {
+ char magic[4]; // Header (0x04034B50) 0
+ u16 minVersion; // Version needed to extract 4
+ u16 flags; // Flags 6
+ u16 comp; // Compression method 8
+ u16 modTime; // Last modified file time A
+ u16 modDate; // Last modified file data C
+ u32 crc32; // CRC32 F
+ u32 compSize; // Compressed size
+ u32 fileSize; // Uncompressed file size
+ u16 nameLength; // Length of the filename
+ u16 extraLength; // Length of any extra data
+ } __attribute__ ((packed));
+
+ char* _zipFile;
+ char _directory[128];
+
+ bool _allFilesVisible;
+
+ FileHeader* _currentFile;
+
+public:
+ ZipFile();
+ ~ZipFile();
+
+ bool isReady();
+
+ // These operations set the current file
+ bool restartFile();
+ bool skipFile();
+ bool findFile(char* search);
+
+ // These return the file's data and information
+ char* getFile();
+ int getFileSize();
+ void getFileName(char* name);
+ bool isDirectory();
+
+ // These set the current directory
+ void changeDirectory(char* name);
+ void changeToRoot();
+ void setAllFilesVisible(bool state) { _allFilesVisible = state; }
+
+ bool currentFileInFolder();
+
+ u16 misaligned16(u16* v);
+ u32 misaligned32(u32* v);
+
+};
+
+
+#endif
+/* ScummVMDS - Scumm Interpreter DS Port
+ * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _ZIPREADER_H_
+#define _ZIPREADER_H_
+#include "stdafx.h"
+#include "portdefs.h"
+#define ZF_SEARCH_START 0x08000000
+#define ZF_SEARCH_END 0x09000000
+#define ZF_SEARCH_STRIDE 16
+
+class ZipFile {
+
+ struct FileHeader {
+ char magic[4]; // Header (0x04034B50) 0
+ u16 minVersion; // Version needed to extract 4
+ u16 flags; // Flags 6
+ u16 comp; // Compression method 8
+ u16 modTime; // Last modified file time A
+ u16 modDate; // Last modified file data C
+ u32 crc32; // CRC32 F
+ u32 compSize; // Compressed size
+ u32 fileSize; // Uncompressed file size
+ u16 nameLength; // Length of the filename
+ u16 extraLength; // Length of any extra data
+ } __attribute__ ((packed));
+
+ char* _zipFile;
+ char _directory[128];
+
+ bool _allFilesVisible;
+
+ FileHeader* _currentFile;
+
+public:
+ ZipFile();
+ ~ZipFile();
+
+ bool isReady();
+
+ // These operations set the current file
+ bool restartFile();
+ bool skipFile();
+ bool findFile(char* search);
+
+ // These return the file's data and information
+ char* getFile();
+ int getFileSize();
+ void getFileName(char* name);
+ bool isDirectory();
+
+ // These set the current directory
+ void changeDirectory(char* name);
+ void changeToRoot();
+ void setAllFilesVisible(bool state) { _allFilesVisible = state; }
+
+ bool currentFileInFolder();
+
+ u16 misaligned16(u16* v);
+ u32 misaligned32(u32* v);
+
+};
+
+
+#endif
diff --git a/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h
new file mode 100644
index 0000000000..10457c2848
--- /dev/null
+++ b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h
@@ -0,0 +1,186 @@
+
+#ifndef SCUMMVM_IPC_INCLUDE
+#define SCUMMVM_IPC_INCLUDE
+
+//////////////////////////////////////////////////////////////////////
+
+#include <nds/jtypes.h>
+#include <nds/ipc.h>
+
+//////////////////////////////////////////////////////////////////////
+
+
+typedef struct _adpcmBuffer {
+ u8* buffer[8];
+ bool filled[8];
+ u8* arm7Buffer[8];
+ bool arm7Dirty[8];
+ bool semaphore;
+} adpcmBuffer;
+
+//////////////////////////////////////////////////////////////////////
+
+typedef struct scummvmTransferRegion {
+ uint32 heartbeat; // counts frames
+
+ int16 touchX, touchY; // TSC X, Y
+ int16 touchXpx, touchYpx; // TSC X, Y pixel values
+ int16 touchZ1, touchZ2; // TSC x-panel measurements
+ uint16 tdiode1, tdiode2; // TSC temperature diodes
+ uint32 temperature; // TSC computed temperature
+
+ uint16 buttons; // X, Y, /PENIRQ buttons
+
+ union {
+ uint8 curtime[8]; // current time response from RTC
+
+ struct {
+ u8 rtc_command;
+ u8 rtc_year; //add 2000 to get 4 digit year
+ u8 rtc_month; //1 to 12
+ u8 rtc_day; //1 to (days in month)
+
+ u8 rtc_incr;
+ u8 rtc_hours; //0 to 11 for AM, 52 to 63 for PM
+ u8 rtc_minutes; //0 to 59
+ u8 rtc_seconds; //0 to 59
+ };
+ };
+
+ uint16 battery; // battery life ?? hopefully. :)
+ uint16 aux; // i have no idea...
+
+ pTransferSound soundData;
+
+ adpcmBuffer adpcm;
+
+
+ // Don't rely on these below, will change or be removed in the future
+ vuint32 mailAddr;
+ vuint32 mailData;
+ vuint8 mailRead;
+ vuint8 mailBusy;
+ vuint32 mailSize;
+
+ bool performArm9SleepMode;
+
+ u32 test;
+ int tweak;
+ bool tweakChanged;
+
+// bool fillSoundFirstHalf;
+// bool fillSoundSecondHalf;
+
+ // These are used for ScummVMs sound output
+ bool fillNeeded[4];
+ int playingSection;
+
+ bool reset;
+
+ // Streaming sound
+ bool streamFillNeeded[4];
+ int streamPlayingSection;
+} scummTransferRegion, * pscummTransferRegion;
+
+//////////////////////////////////////////////////////////////////////
+
+#undef IPC
+#define IPC ((scummTransferRegion volatile *)(0x027FF000))
+
+
+#endif
+
+
+
+#ifndef SCUMMVM_IPC_INCLUDE
+#define SCUMMVM_IPC_INCLUDE
+
+//////////////////////////////////////////////////////////////////////
+
+#include <nds/jtypes.h>
+#include <nds/ipc.h>
+
+//////////////////////////////////////////////////////////////////////
+
+
+typedef struct _adpcmBuffer {
+ u8* buffer[8];
+ bool filled[8];
+ u8* arm7Buffer[8];
+ bool arm7Dirty[8];
+ bool semaphore;
+} adpcmBuffer;
+
+//////////////////////////////////////////////////////////////////////
+
+typedef struct scummvmTransferRegion {
+ uint32 heartbeat; // counts frames
+
+ int16 touchX, touchY; // TSC X, Y
+ int16 touchXpx, touchYpx; // TSC X, Y pixel values
+ int16 touchZ1, touchZ2; // TSC x-panel measurements
+ uint16 tdiode1, tdiode2; // TSC temperature diodes
+ uint32 temperature; // TSC computed temperature
+
+ uint16 buttons; // X, Y, /PENIRQ buttons
+
+ union {
+ uint8 curtime[8]; // current time response from RTC
+
+ struct {
+ u8 rtc_command;
+ u8 rtc_year; //add 2000 to get 4 digit year
+ u8 rtc_month; //1 to 12
+ u8 rtc_day; //1 to (days in month)
+
+ u8 rtc_incr;
+ u8 rtc_hours; //0 to 11 for AM, 52 to 63 for PM
+ u8 rtc_minutes; //0 to 59
+ u8 rtc_seconds; //0 to 59
+ };
+ };
+
+ uint16 battery; // battery life ?? hopefully. :)
+ uint16 aux; // i have no idea...
+
+ pTransferSound soundData;
+
+ adpcmBuffer adpcm;
+
+
+ // Don't rely on these below, will change or be removed in the future
+ vuint32 mailAddr;
+ vuint32 mailData;
+ vuint8 mailRead;
+ vuint8 mailBusy;
+ vuint32 mailSize;
+
+ bool performArm9SleepMode;
+
+ u32 test;
+ int tweak;
+ bool tweakChanged;
+
+// bool fillSoundFirstHalf;
+// bool fillSoundSecondHalf;
+
+ // These are used for ScummVMs sound output
+ bool fillNeeded[4];
+ int playingSection;
+
+ bool reset;
+
+ // Streaming sound
+ bool streamFillNeeded[4];
+ int streamPlayingSection;
+} scummTransferRegion, * pscummTransferRegion;
+
+//////////////////////////////////////////////////////////////////////
+
+#undef IPC
+#define IPC ((scummTransferRegion volatile *)(0x027FF000))
+
+
+#endif
+
+
diff --git a/backends/platform/ds/makefile b/backends/platform/ds/makefile
new file mode 100644
index 0000000000..1a7b38c449
--- /dev/null
+++ b/backends/platform/ds/makefile
@@ -0,0 +1,42 @@
+#SUBDIRS:= `ls | egrep -v '^(CVS|tools)$$'`
+
+
+
+
+export PATH := $(DEVKITARM)/bin:$(PATH)
+
+export portdir = $(CURDIR)/arm9
+export srcdir = $(CURDIR)/../../..
+
+
+SUBDIRS := arm7 arm9
+
+all:
+ @for i in $(SUBDIRS); do if test -d $$i; then make -C $$i; fi; done;
+clean:
+ @for i in $(SUBDIRS); do if test -d $$i; then make -C $$i clean; fi; done;
+
+export:
+ @for i in $(SUBDIRS); do if test -d $$i; then make -C $$i export; fi; done;
+
+#SUBDIRS:= `ls | egrep -v '^(CVS|tools)$$'`
+
+
+
+
+export PATH := $(DEVKITARM)/bin:$(PATH)
+
+export portdir = $(CURDIR)/arm9
+export srcdir = $(CURDIR)/../../..
+
+
+SUBDIRS := arm7 arm9
+
+all:
+ @for i in $(SUBDIRS); do if test -d $$i; then make -C $$i; fi; done;
+clean:
+ @for i in $(SUBDIRS); do if test -d $$i; then make -C $$i clean; fi; done;
+
+export:
+ @for i in $(SUBDIRS); do if test -d $$i; then make -C $$i export; fi; done;
+
diff --git a/common/scummsys.h b/common/scummsys.h
index 3e779d7ee3..8186788c64 100644
--- a/common/scummsys.h
+++ b/common/scummsys.h
@@ -320,6 +320,33 @@
typedef unsigned long int uint32;
typedef signed long int int32;
+#elif defined (__DS__) //NeilM
+ #define CDECL
+ #define SCUMM_NEED_ALIGNMENT
+ #define SCUMM_LITTLE_ENDIAN
+
+ #define scumm_stricmp stricmp
+ #define scumm_strnicmp strnicmp
+ #define CHECK_HEAP
+
+ #define FORCEINLINE inline
+ #define NORETURN __attribute__((__noreturn__))
+ #define GCC_PACK __attribute__((packed))
+ #define _HEAPOK 0
+
+ #include "nds/jtypes.h"
+ #include <stdarg.h>
+ #include <ctype.h>
+ #include <string.h>
+ #include <math.h>
+ #include <time.h>
+
+ #define START_PACK_STRUCTS pack (push, 1)
+ #define END_PACK_STRUCTS pack(pop)
+
+ #define STRINGBUFLEN 256
+ #define SCUMM_LITTLE_ENDIAN
+
#elif defined(_WIN32_WCE)
#define scumm_stricmp stricmp
@@ -356,7 +383,6 @@
#endif
#else
-
#error No system type defined
#endif
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index fb3f217ee6..c482bfe34e 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -27,6 +27,10 @@
#include "graphics/scaler.h"
+#ifdef __DS__
+#include "scummhelp.h"
+#endif
+
#include "gui/about.h"
#include "gui/chooser.h"
#include "gui/eval.h"
@@ -710,7 +714,12 @@ void HelpDialog::displayKeyBindings() {
String titleStr, *keyStr, *dscStr;
+#ifndef __DS__
ScummHelp::updateStrings(_game.id, _game.version, _game.platform, _page, titleStr, keyStr, dscStr);
+#else
+ // DS version has a different help screen
+ DS::updateStrings(_game.id, _game.version, _game.platform, _page, titleStr, keyStr, dscStr);
+#endif
_title->setLabel(titleStr);
for (int i = 0; i < HELP_NUM_LINES; i++) {
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 2881558efa..b355b29eed 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -32,6 +32,9 @@
#include "scumm/resource.h"
#include "scumm/usage_bits.h"
#include "scumm/he/wiz_he.h"
+#ifdef __DS__
+#include "blitters.h"
+#endif
namespace Scumm {
@@ -545,6 +548,9 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
// Handle the text mask in older games; newer (V7/V8) games do not use it anymore.
const byte *text = (byte *)_charset->_textSurface.pixels + x + y * _charset->_textSurface.pitch;
+#ifdef __DS__
+ DS::asmDrawStripToScreen(height, width, text, src, dst, vs->pitch, _screenWidth, _charset->_textSurface.pitch);
+#else
// Compose the text over the game graphics
for (int h = 0; h < height; ++h) {
for (int w = 0; w < width; ++w) {
@@ -557,6 +563,7 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
dst += _screenWidth;
text += _charset->_textSurface.pitch;
}
+#endif
} else {
// Just do a simple blit in V7/V8 games.
blit(dst, _screenWidth, src, vs->pitch, width, height);
@@ -974,6 +981,10 @@ static void fill(byte *dst, int dstPitch, byte color, int w, int h) {
}
static void copy8Col(byte *dst, int dstPitch, const byte *src, int height) {
+
+#ifndef __DS__
+
+
do {
#if defined(SCUMM_NEED_ALIGNMENT)
memcpy(dst, src, 8);
@@ -984,6 +995,10 @@ static void copy8Col(byte *dst, int dstPitch, const byte *src, int height) {
dst += dstPitch;
src += dstPitch;
} while (--height);
+#else
+ DS::asmCopy8Col(dst, dstPitch, src, height);
+#endif
+
}
static void clear8Col(byte *dst, int dstPitch, int height) {
diff --git a/engines/scumm/thumbnail.cpp b/engines/scumm/thumbnail.cpp
index f1854f71c7..af2052fa87 100644
--- a/engines/scumm/thumbnail.cpp
+++ b/engines/scumm/thumbnail.cpp
@@ -104,7 +104,7 @@ Graphics::Surface *ScummEngine::loadThumbnail(Common::InSaveFile *file) {
void ScummEngine::saveThumbnail(Common::OutSaveFile *file) {
Graphics::Surface thumb;
-#ifndef PALMOS_68K
+#if !defined(PALMOS_68K) || !defined(__DS__)
if (!createThumbnailFromScreen(&thumb))
#endif
thumb.create(kThumbnailWidth, kThumbnailHeight2, sizeof(uint16));
diff --git a/gui/options.cpp b/gui/options.cpp
index fd16813c5f..3d03722284 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -37,7 +37,7 @@
#include "sound/mididrv.h"
#include "sound/mixer.h"
-#if (!( defined(PALMOS_MODE) || defined(__DC__) || defined(__GP32__) || defined(__amigaos4__) ) && !defined(_MSC_VER))
+#if (!( defined(PALMOS_MODE) || defined(__DC__) || defined(__GP32__) || defined(__amigaos4__) ) && !defined(_MSC_VER) && !defined(__DS__))
#include <sys/param.h>
#include <unistd.h>
#endif
diff --git a/sound/fmopl.cpp b/sound/fmopl.cpp
index acf17d396b..ff16541732 100644
--- a/sound/fmopl.cpp
+++ b/sound/fmopl.cpp
@@ -35,7 +35,7 @@
#include "common/util.h"
-#if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined (__MAEMO__)
+#if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined (__MAEMO__) || defined(__DS__)
#include "common/config-manager.h"
#endif
@@ -1172,7 +1172,7 @@ FM_OPL *makeAdlibOPL(int rate) {
// We need to emulate one YM3812 chip
int env_bits = FMOPL_ENV_BITS_HQ;
int eg_ent = FMOPL_EG_ENT_HQ;
-#if defined (_WIN32_WCE) || defined(__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined(__MAEMO__)
+#if defined (_WIN32_WCE) || defined(__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined(__MAEMO__) || defined(__DS__)
if (ConfMan.hasKey("FM_high_quality") && ConfMan.getBool("FM_high_quality")) {
env_bits = FMOPL_ENV_BITS_HQ;
eg_ent = FMOPL_EG_ENT_HQ;