NTR_Launcher/ndsbootloader/source/fat.c
ApacheThunder a4e5a00409 Big changes....
* Stage2 launcher UI added. This is a heavily modified HBMenu with
ability to launch carts! This menu can be used to boot a bootloader NDS
file for flascharts that the direct cart launcher fails to boot.
* New audio files added for new menu.
* HBMenu UI loaded by default if booted with no cart inserted.
* INI file folder now located in NTR_Launcher folder instead. A default
ini file from NitroFS (if mount of nitrofs succeeds) will be copied to
SD if one is not present.
* Stage2 launchers are expected to be in NTR Launcehr folder though you
can navigate to any folder you want in the UI so stage2 launchers aren't
the only thing this can be used for. You can also use this as a means of
booting homebrew off SD in NTR mode.
2024-04-10 22:59:52 -05:00

593 lines
15 KiB
C

/*-----------------------------------------------------------------
fat.c
NDS MP
GBAMP NDS Firmware Hack Version 2.12
An NDS aware firmware patch for the GBA Movie Player.
By Michael Chisholm (Chishm)
Filesystem code based on GBAMP_CF.c by Chishm (me).
License:
Copyright (C) 2005 Michael "Chishm" Chisholm
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
If you use this code, please give due credit and email me about your
project at chishm@hotmail.com
------------------------------------------------------------------*/
#include "fat.h"
#include "card.h"
//---------------------------------------------------------------
// FAT constants
#define FILE_LAST 0x00
#define FILE_FREE 0xE5
#define ATTRIB_ARCH 0x20
#define ATTRIB_DIR 0x10
#define ATTRIB_LFN 0x0F
#define ATTRIB_VOL 0x08
#define ATTRIB_HID 0x02
#define ATTRIB_SYS 0x04
#define ATTRIB_RO 0x01
#define FAT16_ROOT_DIR_CLUSTER 0x00
// File Constants
#ifndef EOF
#define EOF -1
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
//-----------------------------------------------------------------
// FAT constants
#define CLUSTER_EOF_16 0xFFFF
#define ATTRIB_ARCH 0x20
#define ATTRIB_DIR 0x10
#define ATTRIB_LFN 0x0F
#define ATTRIB_VOL 0x08
#define ATTRIB_HID 0x02
#define ATTRIB_SYS 0x04
#define ATTRIB_RO 0x01
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Data Structures
#define __PACKED __attribute__ ((__packed__))
// BIOS Parameter Block
typedef struct {
u16 bytesPerSector;
u8 sectorsPerCluster;
u16 reservedSectors;
u8 numFATs;
u16 rootEntries;
u16 numSectorsSmall;
u8 mediaDesc;
u16 sectorsPerFAT;
u16 sectorsPerTrk;
u16 numHeads;
u32 numHiddenSectors;
u32 numSectors;
} __PACKED BIOS_BPB;
// Boot Sector - must be packed
typedef struct
{
u8 jmpBoot[3];
u8 OEMName[8];
BIOS_BPB bpb;
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;
__PACKED u16 bootSig;
} __PACKED BOOT_SEC;
// _Static_assert(sizeof(BOOT_SEC) == 512);
// 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;
// File information - no need to pack
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;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 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
// Locations on card
int discRootDir;
int discRootDirClus;
int discFAT;
int discSecPerFAT;
int discNumSec;
int discData;
int discBytePerSec;
int discSecPerClus;
int discBytePerClus;
enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} discFileSystem;
// Global sector buffer to save on stack space
unsigned char globalBuffer[BYTES_PER_SECTOR];
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//FAT routines
u32 FAT_ClustToSect (u32 cluster) {
return (((cluster-2) * discSecPerClus) + discData);
}
/*-----------------------------------------------------------------
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 (discFileSystem)
{
case FS_UNKNOWN:
nextCluster = CLUSTER_FREE;
break;
case FS_FAT12:
sector = discFAT + (((cluster * 3) / 2) / BYTES_PER_SECTOR);
offset = ((cluster * 3) / 2) % BYTES_PER_SECTOR;
CARD_ReadSector(sector, globalBuffer);
nextCluster = ((u8*) globalBuffer)[offset];
offset++;
if (offset >= BYTES_PER_SECTOR) {
offset = 0;
sector++;
}
CARD_ReadSector(sector, globalBuffer);
nextCluster |= (((u8*) globalBuffer)[offset]) << 8;
if (cluster & 0x01) {
nextCluster = nextCluster >> 4;
} else {
nextCluster &= 0x0FFF;
}
break;
case FS_FAT16:
sector = discFAT + ((cluster << 1) / BYTES_PER_SECTOR);
offset = cluster % (BYTES_PER_SECTOR >> 1);
CARD_ReadSector(sector, globalBuffer);
// read the nextCluster value
nextCluster = ((u16*)globalBuffer)[offset];
if (nextCluster >= 0xFFF7)
{
nextCluster = CLUSTER_EOF;
}
break;
case FS_FAT32:
sector = discFAT + ((cluster << 2) / BYTES_PER_SECTOR);
offset = cluster % (BYTES_PER_SECTOR >> 2);
CARD_ReadSector(sector, globalBuffer);
// read the nextCluster value
nextCluster = (((u32*)globalBuffer)[offset]) & 0x0FFFFFFF;
if (nextCluster >= 0x0FFFFFF7)
{
nextCluster = CLUSTER_EOF;
}
break;
default:
nextCluster = CLUSTER_FREE;
break;
}
return nextCluster;
}
/*-----------------------------------------------------------------
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);
}
/*-----------------------------------------------------------------
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 (bool initCard)
{
int i;
int bootSector;
BOOT_SEC* bootSec;
if (initCard && !CARD_StartUp())
{
return (false);
}
// Read first sector of card
if (!CARD_ReadSector (0, globalBuffer))
{
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;
CARD_ReadSector (bootSector, bootSec);
// Store required information about the file system
if (bootSec->bpb.sectorsPerFAT != 0)
{
discSecPerFAT = bootSec->bpb.sectorsPerFAT;
}
else
{
discSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
}
if (bootSec->bpb.numSectorsSmall != 0)
{
discNumSec = bootSec->bpb.numSectorsSmall;
}
else
{
discNumSec = bootSec->bpb.numSectors;
}
discBytePerSec = BYTES_PER_SECTOR; // Sector size is redefined to be 512 bytes
discSecPerClus = bootSec->bpb.sectorsPerCluster * bootSec->bpb.bytesPerSector / BYTES_PER_SECTOR;
discBytePerClus = discBytePerSec * discSecPerClus;
discFAT = bootSector + bootSec->bpb.reservedSectors;
discRootDir = discFAT + (bootSec->bpb.numFATs * discSecPerFAT);
discData = discRootDir + ((bootSec->bpb.rootEntries * sizeof(DIR_ENT)) / BYTES_PER_SECTOR);
if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 4085)
{
discFileSystem = FS_FAT12;
}
else if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 65525)
{
discFileSystem = FS_FAT16;
}
else
{
discFileSystem = FS_FAT32;
}
if (discFileSystem != FS_FAT32)
{
discRootDirClus = FAT16_ROOT_DIR_CLUSTER;
}
else // Set up for the FAT32 way
{
discRootDirClus = bootSec->extBlock.fat32.rootClus;
// Check if FAT mirroring is enabled
if (!(bootSec->extBlock.fat32.extFlags & 0x80))
{
// Use the active FAT
discFAT = discFAT + ( discSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
}
}
return (true);
}
/*-----------------------------------------------------------------
getBootFileCluster
-----------------------------------------------------------------*/
u32 getBootFileCluster (const char* bootName)
{
DIR_ENT dir;
int firstSector = 0;
bool notFound = false;
bool found = false;
// int maxSectors;
u32 wrkDirCluster = discRootDirClus;
u32 wrkDirSector = 0;
int wrkDirOffset = 0;
int nameOffset;
dir.startCluster = CLUSTER_FREE; // default to no file found
dir.startClusterHigh = CLUSTER_FREE;
// Check if fat has been initialised
if (discBytePerSec == 0)
{
return (CLUSTER_FREE);
}
char *ptr = (char*)bootName;
while (*ptr != '.') ptr++;
int namelen = ptr - bootName;
// maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (discData - discRootDir) : discSecPerClus);
// Scan Dir for correct entry
firstSector = discRootDir;
CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
found = false;
notFound = false;
wrkDirOffset = -1; // Start at entry zero, Compensating for increment
while (!found && !notFound) {
wrkDirOffset++;
if (wrkDirOffset == BYTES_PER_SECTOR / sizeof (DIR_ENT))
{
wrkDirOffset = 0;
wrkDirSector++;
if ((wrkDirSector == discSecPerClus) && (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 == (discData - discRootDir)))
{
notFound = true; // Got to end of root dir
}
CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
}
dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
found = true;
if ((dir.attrib & ATTRIB_DIR) || (dir.attrib & ATTRIB_VOL))
{
found = false;
}
if(namelen<8 && dir.name[namelen]!=0x20) found = false;
for (nameOffset = 0; nameOffset < namelen && found; nameOffset++)
{
if (ucase(dir.name[nameOffset]) != bootName[nameOffset])
found = false;
}
for (nameOffset = 0; nameOffset < 3 && found; nameOffset++)
{
if (ucase(dir.ext[nameOffset]) != bootName[nameOffset+namelen+1])
found = false;
}
if (dir.name[0] == FILE_LAST)
{
notFound = true;
}
}
// If no file is found, return CLUSTER_FREE
if (notFound)
{
return CLUSTER_FREE;
}
return (dir.startCluster | (dir.startClusterHigh << 16));
}
/*-----------------------------------------------------------------
fileRead(buffer, cluster, startOffset, length)
-----------------------------------------------------------------*/
u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length)
{
int curByte;
int curSect;
int dataPos = 0;
int chunks;
int beginBytes;
if (cluster == CLUSTER_FREE || cluster == CLUSTER_EOF)
{
return 0;
}
// Follow cluster list until desired one is found
for (chunks = startOffset / discBytePerClus; chunks > 0; chunks--)
{
cluster = FAT_NextCluster (cluster);
}
// Calculate the sector and byte of the current position,
// and store them
curSect = (startOffset % discBytePerClus) / BYTES_PER_SECTOR;
curByte = startOffset % BYTES_PER_SECTOR;
// Load sector buffer for new position in file
CARD_ReadSector( curSect + FAT_ClustToSect(cluster), globalBuffer);
curSect++;
// Number of bytes needed to read to align with a sector
beginBytes = (BYTES_PER_SECTOR < length + curByte ? (BYTES_PER_SECTOR - curByte) : length);
// Read first part from buffer, to align with sector boundary
for (dataPos = 0 ; dataPos < beginBytes; dataPos++)
{
buffer[dataPos] = globalBuffer[curByte++];
}
// Read in all the 512 byte chunks of the file directly, saving time
for ( chunks = ((int)length - beginBytes) / BYTES_PER_SECTOR; chunks > 0;)
{
int sectorsToRead;
// Move to the next cluster if necessary
if (curSect >= discSecPerClus)
{
curSect = 0;
cluster = FAT_NextCluster (cluster);
}
// Calculate how many sectors to read (read a maximum of discSecPerClus at a time)
sectorsToRead = discSecPerClus - curSect;
if(chunks < sectorsToRead)
sectorsToRead = chunks;
// Read the sectors
CARD_ReadSectors(curSect + FAT_ClustToSect(cluster), sectorsToRead, buffer + dataPos);
chunks -= sectorsToRead;
curSect += sectorsToRead;
dataPos += BYTES_PER_SECTOR * sectorsToRead;
}
// Take care of any bytes left over before end of read
if (dataPos < length)
{
// Update the read buffer
curByte = 0;
if (curSect >= discSecPerClus)
{
curSect = 0;
cluster = FAT_NextCluster (cluster);
}
CARD_ReadSector( curSect + FAT_ClustToSect( cluster), globalBuffer);
// Read in last partial chunk
for (; dataPos < length; dataPos++)
{
buffer[dataPos] = globalBuffer[curByte];
curByte++;
}
}
return dataPos;
}