mirror of
https://github.com/rvtr/TwlNandTool.git
synced 2025-10-31 06:01:08 -04:00
Fixed NAND re-mounting, added AGING test, and merged shared NitroFS files
This commit is contained in:
parent
e0a3ead2f2
commit
f90bc105f0
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
# Something something old releases being hostile and damaging, boo hoo dkp. Maybe don't make extremely breaking changes.
|
||||
container: devkitpro/devkitarm
|
||||
container: devkitpro/devkitarm:20241104
|
||||
name: Build with Docker using devkitARM
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@ -114,4 +114,4 @@ jobs:
|
||||
CONTENT_LENGTH="Content-Length: $(stat -c%s $file)"
|
||||
UPLOAD_URL="https://uploads.github.com/repos/${{ github.repository }}/releases/$ID/assets?name=$(basename $file)"
|
||||
curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL"
|
||||
done
|
||||
done
|
||||
@ -9,3 +9,6 @@ This is the best DSi NAND repair tool out there, offering features such as:
|
||||
- Lots of useful NAND diagnostic info
|
||||
|
||||
Why do these matter? You can create a new working NAND from scratch! No backups needed (but still recommended)!
|
||||
|
||||
## Notes
|
||||
- I am including my own hostile and outdated fork of libfat. This is to block `nand_Startup()` during `fatMount()`. Without this having a NAND re-mount would run `nand_Startup()` more than once and break every NAND R/W function until reboot...
|
||||
@ -188,6 +188,7 @@ int main()
|
||||
fifoSendValue32(FIFO_USER_03, batteryStatus);
|
||||
|
||||
fifoSendValue32(FIFO_USER_01, *(vu8*)0x04004024);
|
||||
|
||||
swiWaitForVBlank();
|
||||
}
|
||||
|
||||
|
||||
@ -39,8 +39,8 @@ endif
|
||||
# all directories are relative to this makefile
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := src src/nand src/nand/polarssl src/nand/twltool
|
||||
INCLUDES := include src/nand
|
||||
SOURCES := src src/nand src/nand/polarssl src/nand/twltool src/lib/libfat/source
|
||||
INCLUDES := include src/nand src/lib/libfat/include
|
||||
DATA := ../data
|
||||
GRAPHICS := fonts
|
||||
#---------------------------------------------------------------------------------
|
||||
@ -63,7 +63,7 @@ LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lfilesystem -lfat -lnds9
|
||||
LIBS := -lfilesystem -lnds9
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
|
||||
122
arm9/src/lib/libfat/include/fat.h
Normal file
122
arm9/src/lib/libfat/include/fat.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
fat.h
|
||||
Simple functionality for startup, mounting and unmounting of FAT-based devices.
|
||||
|
||||
Copyright (c) 2006 - 2012
|
||||
Michael "Chishm" Chisholm
|
||||
Dave "WinterMute" Murphy
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _LIBFAT_H
|
||||
#define _LIBFAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "libfatversion.h"
|
||||
|
||||
// When compiling for NDS, make sure NDS is defined
|
||||
#ifndef NDS
|
||||
#if defined ARM9 || defined ARM7
|
||||
#define NDS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__gamecube__) || defined (__wii__)
|
||||
# include <ogc/disc_io.h>
|
||||
#else
|
||||
# ifdef NDS
|
||||
# include <nds/disc_io.h>
|
||||
# else
|
||||
# include <disc_io.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
Initialise any inserted block-devices.
|
||||
Add the fat device driver to the devoptab, making it available for standard file functions.
|
||||
cacheSize: The number of pages to allocate for each inserted block-device
|
||||
setAsDefaultDevice: if true, make this the default device driver for file operations
|
||||
*/
|
||||
extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
|
||||
|
||||
/*
|
||||
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
||||
*/
|
||||
extern bool fatInitDefault (void);
|
||||
|
||||
/*
|
||||
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||
You can then access the filesystem using "name:/".
|
||||
This will mount the active partition or the first valid partition on the disc,
|
||||
and will use a cache size optimized for the host system.
|
||||
*/
|
||||
extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface, bool isNand);
|
||||
|
||||
/*
|
||||
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||
You can then access the filesystem using "name:/".
|
||||
If startSector = 0, it will mount the active partition of the first valid partition on
|
||||
the disc. Otherwise it will try to mount the partition starting at startSector.
|
||||
cacheSize specifies the number of pages to allocate for the cache.
|
||||
This will not startup the disc, so you need to call interface->startup(); first.
|
||||
*/
|
||||
extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage, bool isNand);
|
||||
|
||||
/*
|
||||
Unmount the partition specified by name.
|
||||
If there are open files, it will attempt to synchronise them to disc.
|
||||
*/
|
||||
extern void fatUnmount (const char* name);
|
||||
|
||||
/*
|
||||
Get Volume Label
|
||||
*/
|
||||
extern void fatGetVolumeLabel (const char* name, char *label);
|
||||
|
||||
// File attributes
|
||||
#define ATTR_ARCHIVE 0x20 // Archive
|
||||
#define ATTR_DIRECTORY 0x10 // Directory
|
||||
#define ATTR_VOLUME 0x08 // Volume
|
||||
#define ATTR_SYSTEM 0x04 // System
|
||||
#define ATTR_HIDDEN 0x02 // Hidden
|
||||
#define ATTR_READONLY 0x01 // Read only
|
||||
|
||||
/*
|
||||
Methods to modify DOS File Attributes
|
||||
*/
|
||||
int FAT_getAttr(const char *file);
|
||||
int FAT_setAttr(const char *file, uint8_t attr );
|
||||
|
||||
#define LIBFAT_FEOS_MULTICWD
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _LIBFAT_H
|
||||
57
arm9/src/lib/libfat/source/bit_ops.h
Normal file
57
arm9/src/lib/libfat/source/bit_ops.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
bit_ops.h
|
||||
Functions for dealing with conversion of data between types
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _BIT_OPS_H
|
||||
#define _BIT_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Functions to deal with little endian values stored in uint8_t arrays
|
||||
-----------------------------------------------------------------*/
|
||||
static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) {
|
||||
return ( item[offset] | (item[offset + 1] << 8));
|
||||
}
|
||||
|
||||
static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) {
|
||||
return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
||||
}
|
||||
|
||||
static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) {
|
||||
item[offset] = (uint8_t) value;
|
||||
item[offset + 1] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) {
|
||||
item[offset] = (uint8_t) value;
|
||||
item[offset + 1] = (uint8_t)(value >> 8);
|
||||
item[offset + 2] = (uint8_t)(value >> 16);
|
||||
item[offset + 3] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
#endif // _BIT_OPS_H
|
||||
323
arm9/src/lib/libfat/source/cache.c
Normal file
323
arm9/src/lib/libfat/source/cache.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
cache.c
|
||||
The cache is not visible to the user. It should be flushed
|
||||
when any file is closed or changes are made to the filesystem.
|
||||
|
||||
This cache implements a least-used-page replacement policy. This will
|
||||
distribute sectors evenly over the pages, so if less than the maximum
|
||||
pages are used at once, they should all eventually remain in the cache.
|
||||
This also has the benefit of throwing out old sectors, so as not to keep
|
||||
too many stale pages around.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cache.h"
|
||||
#include "disc.h"
|
||||
|
||||
#include "mem_allocate.h"
|
||||
#include "bit_ops.h"
|
||||
#include "file_allocation_table.h"
|
||||
|
||||
#define CACHE_FREE UINT_MAX
|
||||
|
||||
CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector) {
|
||||
CACHE* cache;
|
||||
unsigned int i;
|
||||
CACHE_ENTRY* cacheEntries;
|
||||
|
||||
if (numberOfPages < 2) {
|
||||
numberOfPages = 2;
|
||||
}
|
||||
|
||||
if (sectorsPerPage < 8) {
|
||||
sectorsPerPage = 8;
|
||||
}
|
||||
|
||||
cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE));
|
||||
if (cache == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cache->disc = discInterface;
|
||||
cache->endOfPartition = endOfPartition;
|
||||
cache->numberOfPages = numberOfPages;
|
||||
cache->sectorsPerPage = sectorsPerPage;
|
||||
cache->bytesPerSector = bytesPerSector;
|
||||
|
||||
|
||||
cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages);
|
||||
if (cacheEntries == NULL) {
|
||||
_FAT_mem_free (cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < numberOfPages; i++) {
|
||||
cacheEntries[i].sector = CACHE_FREE;
|
||||
cacheEntries[i].count = 0;
|
||||
cacheEntries[i].last_access = 0;
|
||||
cacheEntries[i].dirty = false;
|
||||
cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * bytesPerSector );
|
||||
}
|
||||
|
||||
cache->cacheEntries = cacheEntries;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void _FAT_cache_destructor (CACHE* cache) {
|
||||
unsigned int i;
|
||||
// Clear out cache before destroying it
|
||||
_FAT_cache_flush(cache);
|
||||
|
||||
// Free memory in reverse allocation order
|
||||
for (i = 0; i < cache->numberOfPages; i++) {
|
||||
_FAT_mem_free (cache->cacheEntries[i].cache);
|
||||
}
|
||||
_FAT_mem_free (cache->cacheEntries);
|
||||
_FAT_mem_free (cache);
|
||||
}
|
||||
|
||||
|
||||
static u32 accessCounter = 0;
|
||||
|
||||
static u32 accessTime(){
|
||||
accessCounter++;
|
||||
return accessCounter;
|
||||
}
|
||||
|
||||
|
||||
static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector)
|
||||
{
|
||||
unsigned int i;
|
||||
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||
unsigned int numberOfPages = cache->numberOfPages;
|
||||
unsigned int sectorsPerPage = cache->sectorsPerPage;
|
||||
|
||||
bool foundFree = false;
|
||||
unsigned int oldUsed = 0;
|
||||
unsigned int oldAccess = UINT_MAX;
|
||||
|
||||
for(i=0;i<numberOfPages;i++) {
|
||||
if(sector>=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) {
|
||||
cacheEntries[i].last_access = accessTime();
|
||||
return &(cacheEntries[i]);
|
||||
}
|
||||
|
||||
if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_access<oldAccess)) {
|
||||
if(cacheEntries[i].sector==CACHE_FREE) foundFree = true;
|
||||
oldUsed = i;
|
||||
oldAccess = cacheEntries[i].last_access;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundFree==false && cacheEntries[oldUsed].dirty==true) {
|
||||
if(!_FAT_disc_writeSectors(cache->disc,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL;
|
||||
cacheEntries[oldUsed].dirty = false;
|
||||
}
|
||||
|
||||
sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size
|
||||
sec_t next_page = sector + sectorsPerPage;
|
||||
if(next_page > cache->endOfPartition) next_page = cache->endOfPartition;
|
||||
|
||||
if(!_FAT_disc_readSectors(cache->disc,sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL;
|
||||
|
||||
cacheEntries[oldUsed].sector = sector;
|
||||
cacheEntries[oldUsed].count = next_page-sector;
|
||||
cacheEntries[oldUsed].last_access = accessTime();
|
||||
|
||||
return &(cacheEntries[oldUsed]);
|
||||
}
|
||||
|
||||
bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer)
|
||||
{
|
||||
sec_t sec;
|
||||
sec_t secs_to_read;
|
||||
CACHE_ENTRY *entry;
|
||||
uint8_t *dest = (uint8_t *)buffer;
|
||||
|
||||
while(numSectors>0) {
|
||||
entry = _FAT_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
secs_to_read = entry->count - sec;
|
||||
if(secs_to_read>numSectors) secs_to_read = numSectors;
|
||||
|
||||
memcpy(dest,entry->cache + (sec*cache->bytesPerSector),(secs_to_read*cache->bytesPerSector));
|
||||
|
||||
dest += (secs_to_read*cache->bytesPerSector);
|
||||
sector += secs_to_read;
|
||||
numSectors -= secs_to_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Reads some data from a cache page, determined by the sector number
|
||||
*/
|
||||
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||
{
|
||||
sec_t sec;
|
||||
CACHE_ENTRY *entry;
|
||||
|
||||
if (offset + size > cache->bytesPerSector) return false;
|
||||
|
||||
entry = _FAT_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
memcpy(buffer,entry->cache + ((sec*cache->bytesPerSector) + offset),size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) {
|
||||
uint8_t buf[4];
|
||||
if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
||||
|
||||
switch(num_bytes) {
|
||||
case 1: *value = buf[0]; break;
|
||||
case 2: *value = u8array_to_u16(buf,0); break;
|
||||
case 4: *value = u8array_to_u32(buf,0); break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Writes some data to a cache page, making sure it is loaded into memory first.
|
||||
*/
|
||||
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||
{
|
||||
sec_t sec;
|
||||
CACHE_ENTRY *entry;
|
||||
|
||||
if (offset + size > cache->bytesPerSector) return false;
|
||||
|
||||
entry = _FAT_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size);
|
||||
|
||||
entry->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) {
|
||||
uint8_t buf[4] = {0, 0, 0, 0};
|
||||
|
||||
switch(size) {
|
||||
case 1: buf[0] = value; break;
|
||||
case 2: u16_to_u8array(buf, 0, value); break;
|
||||
case 4: u32_to_u8array(buf, 0, value); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
return _FAT_cache_writePartialSector(cache, buf, sector, offset, size);
|
||||
}
|
||||
|
||||
/*
|
||||
Writes some data to a cache page, zeroing out the page first
|
||||
*/
|
||||
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||
{
|
||||
sec_t sec;
|
||||
CACHE_ENTRY *entry;
|
||||
|
||||
if (offset + size > cache->bytesPerSector) return false;
|
||||
|
||||
entry = _FAT_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
memset(entry->cache + (sec*cache->bytesPerSector),0,cache->bytesPerSector);
|
||||
memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size);
|
||||
|
||||
entry->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
|
||||
{
|
||||
sec_t sec;
|
||||
sec_t secs_to_write;
|
||||
CACHE_ENTRY* entry;
|
||||
const uint8_t *src = (const uint8_t *)buffer;
|
||||
|
||||
while(numSectors>0)
|
||||
{
|
||||
entry = _FAT_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
secs_to_write = entry->count - sec;
|
||||
if(secs_to_write>numSectors) secs_to_write = numSectors;
|
||||
|
||||
memcpy(entry->cache + (sec*cache->bytesPerSector),src,(secs_to_write*cache->bytesPerSector));
|
||||
|
||||
src += (secs_to_write*cache->bytesPerSector);
|
||||
sector += secs_to_write;
|
||||
numSectors -= secs_to_write;
|
||||
|
||||
entry->dirty = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Flushes all dirty pages to disc, clearing the dirty flag.
|
||||
*/
|
||||
bool _FAT_cache_flush (CACHE* cache) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < cache->numberOfPages; i++) {
|
||||
if (cache->cacheEntries[i].dirty) {
|
||||
if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cache->cacheEntries[i].dirty = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _FAT_cache_invalidate (CACHE* cache) {
|
||||
unsigned int i;
|
||||
_FAT_cache_flush(cache);
|
||||
for (i = 0; i < cache->numberOfPages; i++) {
|
||||
cache->cacheEntries[i].sector = CACHE_FREE;
|
||||
cache->cacheEntries[i].last_access = 0;
|
||||
cache->cacheEntries[i].count = 0;
|
||||
cache->cacheEntries[i].dirty = false;
|
||||
}
|
||||
}
|
||||
128
arm9/src/lib/libfat/source/cache.h
Normal file
128
arm9/src/lib/libfat/source/cache.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
cache.h
|
||||
The cache is not visible to the user. It should be flushed
|
||||
when any file is closed or changes are made to the filesystem.
|
||||
|
||||
This cache implements a least-used-page replacement policy. This will
|
||||
distribute sectors evenly over the pages, so if less than the maximum
|
||||
pages are used at once, they should all eventually remain in the cache.
|
||||
This also has the benefit of throwing out old sectors, so as not to keep
|
||||
too many stale pages around.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _CACHE_H
|
||||
#define _CACHE_H
|
||||
|
||||
#include "common.h"
|
||||
#include "disc.h"
|
||||
|
||||
typedef struct {
|
||||
sec_t sector;
|
||||
unsigned int count;
|
||||
unsigned int last_access;
|
||||
bool dirty;
|
||||
uint8_t* cache;
|
||||
} CACHE_ENTRY;
|
||||
|
||||
typedef struct {
|
||||
const DISC_INTERFACE* disc;
|
||||
sec_t endOfPartition;
|
||||
unsigned int numberOfPages;
|
||||
unsigned int sectorsPerPage;
|
||||
unsigned int bytesPerSector;
|
||||
CACHE_ENTRY* cacheEntries;
|
||||
} CACHE;
|
||||
|
||||
/*
|
||||
Read data from a sector in the cache
|
||||
If the sector is not in the cache, it will be swapped in
|
||||
offset is the position to start reading from
|
||||
size is the amount of data to read
|
||||
Precondition: offset + size <= BYTES_PER_READ
|
||||
*/
|
||||
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||
|
||||
bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
||||
|
||||
/*
|
||||
Write data to a sector in the cache
|
||||
If the sector is not in the cache, it will be swapped in.
|
||||
When the sector is swapped out, the data will be written to the disc
|
||||
offset is the position to start writing to
|
||||
size is the amount of data to write
|
||||
Precondition: offset + size <= BYTES_PER_READ
|
||||
*/
|
||||
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||
|
||||
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes);
|
||||
|
||||
/*
|
||||
Write data to a sector in the cache, zeroing the sector first
|
||||
If the sector is not in the cache, it will be swapped in.
|
||||
When the sector is swapped out, the data will be written to the disc
|
||||
offset is the position to start writing to
|
||||
size is the amount of data to write
|
||||
Precondition: offset + size <= BYTES_PER_READ
|
||||
*/
|
||||
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||
|
||||
/*
|
||||
Read several sectors from the cache
|
||||
*/
|
||||
bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer);
|
||||
|
||||
/*
|
||||
Read a full sector from the cache
|
||||
*/
|
||||
static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) {
|
||||
return _FAT_cache_readPartialSector (cache, buffer, sector, 0, cache->bytesPerSector);
|
||||
}
|
||||
|
||||
/*
|
||||
Write a full sector to the cache
|
||||
*/
|
||||
static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) {
|
||||
return _FAT_cache_writePartialSector (cache, buffer, sector, 0, cache->bytesPerSector);
|
||||
}
|
||||
|
||||
bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer);
|
||||
|
||||
/*
|
||||
Write any dirty sectors back to disc and clear out the contents of the cache
|
||||
*/
|
||||
bool _FAT_cache_flush (CACHE* cache);
|
||||
|
||||
/*
|
||||
Clear out the contents of the cache without writing any dirty sectors first
|
||||
*/
|
||||
void _FAT_cache_invalidate (CACHE* cache);
|
||||
|
||||
CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector);
|
||||
|
||||
void _FAT_cache_destructor (CACHE* cache);
|
||||
|
||||
#endif // _CACHE_H
|
||||
|
||||
84
arm9/src/lib/libfat/source/common.h
Normal file
84
arm9/src/lib/libfat/source/common.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
common.h
|
||||
Common definitions and included files for the FATlib
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#include <fat.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// When compiling for NDS, make sure NDS is defined
|
||||
#ifndef NDS
|
||||
#if defined ARM9 || defined ARM7
|
||||
#define NDS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Platform specific includes
|
||||
#if defined(__gamecube__) || defined (__wii__)
|
||||
#include <gctypes.h>
|
||||
#include <ogc/disc_io.h>
|
||||
#include <gccore.h>
|
||||
#elif defined(NDS)
|
||||
#include <nds/ndstypes.h>
|
||||
#include <nds/system.h>
|
||||
#include <nds/disc_io.h>
|
||||
#elif defined(GBA)
|
||||
#include <gba_types.h>
|
||||
#include <disc_io.h>
|
||||
#elif defined(GP2X)
|
||||
#include <gp2xtypes.h>
|
||||
#include <disc_io.h>
|
||||
#endif
|
||||
|
||||
// Platform specific options
|
||||
#if defined (__wii__)
|
||||
#define DEFAULT_CACHE_PAGES 4
|
||||
#define DEFAULT_SECTORS_PAGE 64
|
||||
#define USE_LWP_LOCK
|
||||
#define USE_RTC_TIME
|
||||
#elif defined (__gamecube__)
|
||||
#define DEFAULT_CACHE_PAGES 4
|
||||
#define DEFAULT_SECTORS_PAGE 64
|
||||
#define USE_LWP_LOCK
|
||||
#define USE_RTC_TIME
|
||||
#elif defined (NDS)
|
||||
#define DEFAULT_CACHE_PAGES 16
|
||||
#define DEFAULT_SECTORS_PAGE 8
|
||||
#define USE_RTC_TIME
|
||||
#elif defined (GBA)
|
||||
#define DEFAULT_CACHE_PAGES 2
|
||||
#define DEFAULT_SECTORS_PAGE 8
|
||||
#define LIMIT_SECTORS 128
|
||||
#elif defined (GP2X)
|
||||
#define DEFAULT_CACHE_PAGES 16
|
||||
#define DEFAULT_SECTORS_PAGE 8
|
||||
#endif
|
||||
|
||||
#endif // _COMMON_H
|
||||
1124
arm9/src/lib/libfat/source/directory.c
Normal file
1124
arm9/src/lib/libfat/source/directory.c
Normal file
File diff suppressed because it is too large
Load Diff
181
arm9/src/lib/libfat/source/directory.h
Normal file
181
arm9/src/lib/libfat/source/directory.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
directory.h
|
||||
Reading, writing and manipulation of the directory structure on
|
||||
a FAT partition
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DIRECTORY_H
|
||||
#define _DIRECTORY_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "partition.h"
|
||||
|
||||
#define DIR_ENTRY_DATA_SIZE 0x20
|
||||
#define MAX_LFN_LENGTH 256
|
||||
#define MAX_ALIAS_LENGTH 13
|
||||
#define LFN_ENTRY_LENGTH 13
|
||||
#define ALIAS_ENTRY_LENGTH 11
|
||||
#define MAX_ALIAS_EXT_LENGTH 3
|
||||
#define MAX_ALIAS_PRI_LENGTH 8
|
||||
#define MAX_NUMERIC_TAIL 999999
|
||||
#define FAT16_ROOT_DIR_CLUSTER 0
|
||||
|
||||
#define DIR_SEPARATOR '/'
|
||||
|
||||
// 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
|
||||
|
||||
#define CASE_LOWER_EXT 0x10 // WinNT lowercase extension
|
||||
#define CASE_LOWER_BASE 0x08 // WinNT lowercase basename
|
||||
|
||||
typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cluster;
|
||||
sec_t sector;
|
||||
int32_t offset;
|
||||
} DIR_ENTRY_POSITION;
|
||||
|
||||
typedef struct {
|
||||
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
||||
DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
||||
DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry
|
||||
char filename[NAME_MAX];
|
||||
} DIR_ENTRY;
|
||||
|
||||
// Directory entry offsets
|
||||
enum DIR_ENTRY_offset {
|
||||
DIR_ENTRY_name = 0x00,
|
||||
DIR_ENTRY_extension = 0x08,
|
||||
DIR_ENTRY_attributes = 0x0B,
|
||||
DIR_ENTRY_caseInfo = 0x0C,
|
||||
DIR_ENTRY_cTime_ms = 0x0D,
|
||||
DIR_ENTRY_cTime = 0x0E,
|
||||
DIR_ENTRY_cDate = 0x10,
|
||||
DIR_ENTRY_aDate = 0x12,
|
||||
DIR_ENTRY_clusterHigh = 0x14,
|
||||
DIR_ENTRY_mTime = 0x16,
|
||||
DIR_ENTRY_mDate = 0x18,
|
||||
DIR_ENTRY_cluster = 0x1A,
|
||||
DIR_ENTRY_fileSize = 0x1C
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the file specified by entry is a directory
|
||||
*/
|
||||
static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) {
|
||||
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0);
|
||||
}
|
||||
|
||||
static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) {
|
||||
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0);
|
||||
}
|
||||
|
||||
static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) {
|
||||
return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') ||
|
||||
((entry->filename[1] == '.') && entry->filename[2] == '\0')));
|
||||
}
|
||||
|
||||
/*
|
||||
Reads the first directory entry from the directory starting at dirCluster
|
||||
Places result in entry
|
||||
entry will be destroyed even if no directory entry is found
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
||||
|
||||
/*
|
||||
Reads the next directory entry after the one already pointed to by entry
|
||||
Places result in entry
|
||||
entry will be destroyed even if no directory entry is found
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry);
|
||||
|
||||
/*
|
||||
Gets the directory entry corrsponding to the supplied path
|
||||
entry will be destroyed even if no directory entry is found
|
||||
pathEnd specifies the end of the path string, for cutting strings short if needed
|
||||
specify NULL to use the full length of path
|
||||
pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR
|
||||
after pathEND.
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd);
|
||||
|
||||
/*
|
||||
Changes the current directory to the one specified by path
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_chdir (PARTITION* partition, const char* path);
|
||||
|
||||
/*
|
||||
Removes the directory entry specified by entry
|
||||
Assumes that entry is valid
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry);
|
||||
|
||||
/*
|
||||
Add a directory entry to the directory specified by dirCluster
|
||||
The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are
|
||||
updated with the new directory entry position and alias.
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
||||
|
||||
/*
|
||||
Get the start cluster of a file from it's entry data
|
||||
*/
|
||||
uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData);
|
||||
|
||||
/*
|
||||
Fill in the file name and entry data of DIR_ENTRY* entry.
|
||||
Assumes that the entry's dataStart and dataEnd are correct
|
||||
Returns true on success, false on failure
|
||||
*/
|
||||
bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry);
|
||||
|
||||
/*
|
||||
Fill in a stat struct based on a file entry
|
||||
*/
|
||||
void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st);
|
||||
|
||||
/*
|
||||
Get volume label
|
||||
*/
|
||||
bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label);
|
||||
|
||||
#endif // _DIRECTORY_H
|
||||
125
arm9/src/lib/libfat/source/disc.c
Executable file
125
arm9/src/lib/libfat/source/disc.c
Executable file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
disc.c
|
||||
Interface to the low level disc functions. Used by the higher level
|
||||
file system code.
|
||||
|
||||
Copyright (c) 2008 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "disc.h"
|
||||
|
||||
/*
|
||||
The list of interfaces consists of a series of name/interface pairs.
|
||||
The interface is returned via a simple function. This allows for
|
||||
platforms where the interface has to be "assembled" before it can
|
||||
be used, like DLDI on the NDS. For cases where a simple struct
|
||||
is available, wrapper functions are used.
|
||||
The list is terminated by a NULL/NULL entry.
|
||||
*/
|
||||
|
||||
/* ====================== Wii ====================== */
|
||||
#if defined (__wii__)
|
||||
#include <sdcard/wiisd_io.h>
|
||||
#include <ogc/usbstorage.h>
|
||||
#include <sdcard/gcsd.h>
|
||||
|
||||
static const DISC_INTERFACE* get_io_wiisd (void) {
|
||||
return &__io_wiisd;
|
||||
}
|
||||
static const DISC_INTERFACE* get_io_usbstorage (void) {
|
||||
return &__io_usbstorage;
|
||||
}
|
||||
|
||||
static const DISC_INTERFACE* get_io_gcsda (void) {
|
||||
return &__io_gcsda;
|
||||
}
|
||||
|
||||
static const DISC_INTERFACE* get_io_gcsdb (void) {
|
||||
return &__io_gcsdb;
|
||||
}
|
||||
|
||||
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||
{"sd", get_io_wiisd},
|
||||
{"usb", get_io_usbstorage},
|
||||
{"carda", get_io_gcsda},
|
||||
{"cardb", get_io_gcsdb},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* ==================== Gamecube ==================== */
|
||||
#elif defined (__gamecube__)
|
||||
#include <sdcard/gcsd.h>
|
||||
|
||||
static const DISC_INTERFACE* get_io_gcsd2 (void) {
|
||||
return &__io_gcsd2;
|
||||
}
|
||||
|
||||
static const DISC_INTERFACE* get_io_gcsdb (void) {
|
||||
return &__io_gcsdb;
|
||||
}
|
||||
|
||||
static const DISC_INTERFACE* get_io_gcsda (void) {
|
||||
return &__io_gcsda;
|
||||
}
|
||||
|
||||
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||
{"sd", get_io_gcsd2},
|
||||
{"carda", get_io_gcsda},
|
||||
{"cardb", get_io_gcsdb},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* ====================== NDS ====================== */
|
||||
#elif defined (NDS)
|
||||
#include <nds/system.h>
|
||||
#include <nds/memory.h>
|
||||
#include <nds/arm9/dldi.h>
|
||||
|
||||
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||
{"sd", get_io_dsisd},
|
||||
{"fat", dldiGetInternal},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* ====================== GBA ====================== */
|
||||
#elif defined (GBA)
|
||||
#include <disc.h>
|
||||
|
||||
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||
{"fat", discGetInterface},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* ====================== GP2X ====================== */
|
||||
#elif defined (GP2X)
|
||||
#include <disc_io.h>
|
||||
|
||||
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||
{"sd", get_io_gp2xsd},
|
||||
{NULL, NULL}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
110
arm9/src/lib/libfat/source/disc.h
Normal file
110
arm9/src/lib/libfat/source/disc.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
disc.h
|
||||
Interface to the low level disc functions. Used by the higher level
|
||||
file system code.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _DISC_H
|
||||
#define _DISC_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
A list of all default devices to try at startup,
|
||||
terminated by a {NULL,NULL} entry.
|
||||
*/
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const DISC_INTERFACE* (*getInterface)(void);
|
||||
} INTERFACE_ID;
|
||||
extern const INTERFACE_ID _FAT_disc_interfaces[];
|
||||
|
||||
/*
|
||||
Check if a disc is inserted
|
||||
Return true if a disc is inserted and ready, false otherwise
|
||||
*/
|
||||
static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) {
|
||||
return disc->isInserted();
|
||||
}
|
||||
|
||||
/*
|
||||
Read numSectors sectors from a disc, starting at sector.
|
||||
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
||||
else it is at least 1
|
||||
sector is 0 or greater
|
||||
buffer is a pointer to the memory to fill
|
||||
*/
|
||||
static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) {
|
||||
return disc->readSectors (sector, numSectors, buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
Write numSectors sectors to a disc, starting at sector.
|
||||
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
||||
else it is at least 1
|
||||
sector is 0 or greater
|
||||
buffer is a pointer to the memory to read from
|
||||
*/
|
||||
static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) {
|
||||
return disc->writeSectors (sector, numSectors, buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
Reset the card back to a ready state
|
||||
*/
|
||||
static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) {
|
||||
return disc->clearStatus();
|
||||
}
|
||||
|
||||
/*
|
||||
Initialise the disc to a state ready for data reading or writing
|
||||
*/
|
||||
static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) {
|
||||
return disc->startup();
|
||||
}
|
||||
|
||||
/*
|
||||
Put the disc in a state ready for power down.
|
||||
Complete any pending writes and disable the disc if necessary
|
||||
*/
|
||||
static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) {
|
||||
return disc->shutdown();
|
||||
}
|
||||
|
||||
/*
|
||||
Return a 32 bit value unique to each type of interface
|
||||
*/
|
||||
static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) {
|
||||
return disc->ioType;
|
||||
}
|
||||
|
||||
/*
|
||||
Return a 32 bit value that specifies the capabilities of the disc
|
||||
*/
|
||||
static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) {
|
||||
return disc->features;
|
||||
}
|
||||
|
||||
#endif // _DISC_H
|
||||
633
arm9/src/lib/libfat/source/fatdir.c
Normal file
633
arm9/src/lib/libfat/source/fatdir.c
Normal file
@ -0,0 +1,633 @@
|
||||
/*
|
||||
fatdir.c
|
||||
|
||||
Functions used by the newlib disc stubs to interface with
|
||||
this library
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/iosupport.h>
|
||||
|
||||
#include "fatdir.h"
|
||||
|
||||
#include "cache.h"
|
||||
#include "file_allocation_table.h"
|
||||
#include "partition.h"
|
||||
#include "directory.h"
|
||||
#include "bit_ops.h"
|
||||
#include "filetime.h"
|
||||
#include "lock.h"
|
||||
|
||||
|
||||
int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
|
||||
PARTITION* partition = NULL;
|
||||
DIR_ENTRY dirEntry;
|
||||
|
||||
// Get the partition this file is on
|
||||
partition = _FAT_partition_getPartitionFromPath (path);
|
||||
if (partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr (path, ':') != NULL) {
|
||||
path = strchr (path, ':') + 1;
|
||||
}
|
||||
if (strchr (path, ':') != NULL) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
// Search for the file on the disc
|
||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fill in the stat struct
|
||||
_FAT_directory_entryStat (partition, &dirEntry, st);
|
||||
|
||||
_FAT_unlock(&partition->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink) {
|
||||
r->_errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _FAT_unlinkCommon (struct _reent *r, const char *path, bool isRmDir) {
|
||||
PARTITION* partition = NULL;
|
||||
DIR_ENTRY dirEntry;
|
||||
DIR_ENTRY dirContents;
|
||||
uint32_t cluster;
|
||||
bool nextEntry;
|
||||
bool errorOccured = false;
|
||||
|
||||
// Get the partition this directory is on
|
||||
partition = _FAT_partition_getPartitionFromPath (path);
|
||||
if (partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make sure we aren't trying to write to a read-only disc
|
||||
if (partition->readOnly) {
|
||||
r->_errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr (path, ':') != NULL) {
|
||||
path = strchr (path, ':') + 1;
|
||||
}
|
||||
if (strchr (path, ':') != NULL) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
// Search for the file on the disc
|
||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
|
||||
|
||||
|
||||
// If this is a directory, make sure it is empty
|
||||
if (_FAT_directory_isDirectory (&dirEntry)) {
|
||||
if (!isRmDir) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EISDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster);
|
||||
|
||||
while (nextEntry) {
|
||||
if (!_FAT_directory_isDot (&dirContents)) {
|
||||
// The directory had something in it that isn't a reference to itself or it's parent
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOTEMPTY;
|
||||
return -1;
|
||||
}
|
||||
nextEntry = _FAT_directory_getNextEntry (partition, &dirContents);
|
||||
}
|
||||
} else if (isRmDir) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_FAT_fat_isValidCluster(partition, cluster)) {
|
||||
// Remove the cluster chain for this file
|
||||
if (!_FAT_fat_clearLinks (partition, cluster)) {
|
||||
r->_errno = EIO;
|
||||
errorOccured = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the directory entry for this file
|
||||
if (!_FAT_directory_removeEntry (partition, &dirEntry)) {
|
||||
r->_errno = EIO;
|
||||
errorOccured = true;
|
||||
}
|
||||
|
||||
// Flush any sectors in the disc cache
|
||||
if (!_FAT_cache_flush(partition->cache)) {
|
||||
r->_errno = EIO;
|
||||
errorOccured = true;
|
||||
}
|
||||
|
||||
_FAT_unlock(&partition->lock);
|
||||
if (errorOccured) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int _FAT_unlink_r (struct _reent *r, const char *path) {
|
||||
return _FAT_unlinkCommon (r, path, false);
|
||||
}
|
||||
|
||||
int _FAT_chdir_r (struct _reent *r, const char *path) {
|
||||
PARTITION* partition = NULL;
|
||||
|
||||
// Get the partition this directory is on
|
||||
partition = _FAT_partition_getPartitionFromPath (path);
|
||||
if (partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr (path, ':') != NULL) {
|
||||
path = strchr (path, ':') + 1;
|
||||
}
|
||||
if (strchr (path, ':') != NULL) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
// Try changing directory
|
||||
if (_FAT_directory_chdir (partition, path)) {
|
||||
// Successful
|
||||
_FAT_unlock(&partition->lock);
|
||||
return 0;
|
||||
} else {
|
||||
// Failed
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
||||
PARTITION* partition = NULL;
|
||||
DIR_ENTRY oldDirEntry;
|
||||
DIR_ENTRY newDirEntry;
|
||||
const char *pathEnd;
|
||||
uint32_t dirCluster;
|
||||
|
||||
// Get the partition this directory is on
|
||||
partition = _FAT_partition_getPartitionFromPath (oldName);
|
||||
if (partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
// Make sure the same partition is used for the old and new names
|
||||
if (partition != _FAT_partition_getPartitionFromPath (newName)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EXDEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make sure we aren't trying to write to a read-only disc
|
||||
if (partition->readOnly) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr (oldName, ':') != NULL) {
|
||||
oldName = strchr (oldName, ':') + 1;
|
||||
}
|
||||
if (strchr (oldName, ':') != NULL) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (strchr (newName, ':') != NULL) {
|
||||
newName = strchr (newName, ':') + 1;
|
||||
}
|
||||
if (strchr (newName, ':') != NULL) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Search for the file on the disc
|
||||
if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make sure there is no existing file / directory with the new name
|
||||
if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the new file entry
|
||||
// Get the directory it has to go in
|
||||
pathEnd = strrchr (newName, DIR_SEPARATOR);
|
||||
if (pathEnd == NULL) {
|
||||
// No path was specified
|
||||
dirCluster = partition->cwdCluster;
|
||||
pathEnd = newName;
|
||||
} else {
|
||||
// Path was specified -- get the right dirCluster
|
||||
// Recycling newDirEntry, since it needs to be recreated anyway
|
||||
if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) ||
|
||||
!_FAT_directory_isDirectory(&newDirEntry)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
dirCluster = _FAT_directory_entryGetCluster (partition, newDirEntry.entryData);
|
||||
// Move the pathEnd past the last DIR_SEPARATOR
|
||||
pathEnd += 1;
|
||||
}
|
||||
|
||||
// Copy the entry data
|
||||
memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY));
|
||||
|
||||
// Set the new name
|
||||
strncpy (newDirEntry.filename, pathEnd, NAME_MAX - 1);
|
||||
|
||||
// Write the new entry
|
||||
if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Remove the old entry
|
||||
if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Flush any sectors in the disc cache
|
||||
if (!_FAT_cache_flush (partition->cache)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_unlock(&partition->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
||||
PARTITION* partition = NULL;
|
||||
bool fileExists;
|
||||
DIR_ENTRY dirEntry;
|
||||
const char* pathEnd;
|
||||
uint32_t parentCluster, dirCluster;
|
||||
uint8_t newEntryData[DIR_ENTRY_DATA_SIZE];
|
||||
|
||||
partition = _FAT_partition_getPartitionFromPath (path);
|
||||
if (partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr (path, ':') != NULL) {
|
||||
path = strchr (path, ':') + 1;
|
||||
}
|
||||
if (strchr (path, ':') != NULL) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
// Search for the file/directory on the disc
|
||||
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
|
||||
|
||||
// Make sure it doesn't exist
|
||||
if (fileExists) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (partition->readOnly) {
|
||||
// We can't write to a read-only partition
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the directory it has to go in
|
||||
pathEnd = strrchr (path, DIR_SEPARATOR);
|
||||
if (pathEnd == NULL) {
|
||||
// No path was specified
|
||||
parentCluster = partition->cwdCluster;
|
||||
pathEnd = path;
|
||||
} else {
|
||||
// Path was specified -- get the right parentCluster
|
||||
// Recycling dirEntry, since it needs to be recreated anyway
|
||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
|
||||
!_FAT_directory_isDirectory(&dirEntry)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
parentCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
|
||||
// Move the pathEnd past the last DIR_SEPARATOR
|
||||
pathEnd += 1;
|
||||
}
|
||||
// Create the entry data
|
||||
strncpy (dirEntry.filename, pathEnd, NAME_MAX - 1);
|
||||
memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
|
||||
|
||||
// Set the creation time and date
|
||||
dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
|
||||
|
||||
// Set the directory attribute
|
||||
dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
|
||||
|
||||
// Get a cluster for the new directory
|
||||
dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE);
|
||||
if (!_FAT_fat_isValidCluster(partition, dirCluster)) {
|
||||
// No space left on disc for the cluster
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cluster, dirCluster);
|
||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
|
||||
|
||||
// Write the new directory's entry to it's parent
|
||||
if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the dot entry within the directory
|
||||
memset (newEntryData, 0, DIR_ENTRY_DATA_SIZE);
|
||||
memset (newEntryData, ' ', 11);
|
||||
newEntryData[DIR_ENTRY_name] = '.';
|
||||
newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
|
||||
u16_to_u8array (newEntryData, DIR_ENTRY_cluster, dirCluster);
|
||||
u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
|
||||
|
||||
// Write it to the directory, erasing that sector in the process
|
||||
_FAT_cache_eraseWritePartialSector ( partition->cache, newEntryData,
|
||||
_FAT_fat_clusterToSector (partition, dirCluster), 0, DIR_ENTRY_DATA_SIZE);
|
||||
|
||||
|
||||
// Create the double dot entry within the directory
|
||||
|
||||
// if ParentDir == Rootdir then ".."" always link to Cluster 0
|
||||
if(parentCluster == partition->rootDirCluster)
|
||||
parentCluster = FAT16_ROOT_DIR_CLUSTER;
|
||||
|
||||
newEntryData[DIR_ENTRY_name + 1] = '.';
|
||||
u16_to_u8array (newEntryData, DIR_ENTRY_cluster, parentCluster);
|
||||
u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16);
|
||||
|
||||
// Write it to the directory
|
||||
_FAT_cache_writePartialSector ( partition->cache, newEntryData,
|
||||
_FAT_fat_clusterToSector (partition, dirCluster), DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
|
||||
|
||||
// Flush any sectors in the disc cache
|
||||
if (!_FAT_cache_flush(partition->cache)) {
|
||||
_FAT_unlock(&partition->lock);
|
||||
r->_errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_unlock(&partition->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _FAT_rmdir_r (struct _reent *r, const char *path) {
|
||||
return _FAT_unlinkCommon (r, path, true);
|
||||
}
|
||||
|
||||
int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
||||
{
|
||||
PARTITION* partition = NULL;
|
||||
unsigned int freeClusterCount;
|
||||
|
||||
// Get the partition of the requested path
|
||||
partition = _FAT_partition_getPartitionFromPath (path);
|
||||
if (partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
if(partition->filesysType == FS_FAT32) {
|
||||
// Sync FSinfo block
|
||||
_FAT_partition_readFSinfo(partition);
|
||||
freeClusterCount = partition->fat.numberFreeCluster;
|
||||
} else {
|
||||
freeClusterCount = _FAT_fat_freeClusterCount (partition);
|
||||
}
|
||||
|
||||
// FAT clusters = POSIX blocks
|
||||
buf->f_bsize = partition->bytesPerCluster; // File system block size.
|
||||
buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size.
|
||||
|
||||
buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize.
|
||||
buf->f_bfree = freeClusterCount; // Total number of free blocks.
|
||||
buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process.
|
||||
|
||||
// Treat requests for info on inodes as clusters
|
||||
buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers.
|
||||
buf->f_ffree = freeClusterCount; // Total number of free file serial numbers.
|
||||
buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process.
|
||||
|
||||
// File system ID. 32bit ioType value
|
||||
buf->f_fsid = _FAT_disc_hostType(partition->disc);
|
||||
|
||||
// Bit mask of f_flag values.
|
||||
buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */
|
||||
| (partition->readOnly ? ST_RDONLY /* Read only file system */ : 0 ) ;
|
||||
// Maximum filename length.
|
||||
buf->f_namemax = NAME_MAX;
|
||||
|
||||
_FAT_unlock(&partition->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
|
||||
DIR_ENTRY dirEntry;
|
||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||
bool fileExists;
|
||||
|
||||
state->partition = _FAT_partition_getPartitionFromPath (path);
|
||||
if (state->partition == NULL) {
|
||||
r->_errno = ENODEV;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr (path, ':') != NULL) {
|
||||
path = strchr (path, ':') + 1;
|
||||
}
|
||||
if (strchr (path, ':') != NULL) {
|
||||
r->_errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_FAT_lock(&state->partition->lock);
|
||||
|
||||
// Get the start cluster of the directory
|
||||
fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL);
|
||||
|
||||
if (!fileExists) {
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
r->_errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Make sure it is a directory
|
||||
if (! _FAT_directory_isDirectory (&dirEntry)) {
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
r->_errno = ENOTDIR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Save the start cluster for use when resetting the directory data
|
||||
state->startCluster = _FAT_directory_entryGetCluster (state->partition, dirEntry.entryData);
|
||||
|
||||
// Get the first entry for use with a call to dirnext
|
||||
state->validEntry =
|
||||
_FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
|
||||
|
||||
// We are now using this entry
|
||||
state->inUse = true;
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
return (DIR_ITER*) state;
|
||||
}
|
||||
|
||||
int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) {
|
||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||
|
||||
_FAT_lock(&state->partition->lock);
|
||||
|
||||
// Make sure we are still using this entry
|
||||
if (!state->inUse) {
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
r->_errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the first entry for use with a call to dirnext
|
||||
state->validEntry =
|
||||
_FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
|
||||
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||
|
||||
_FAT_lock(&state->partition->lock);
|
||||
|
||||
// Make sure we are still using this entry
|
||||
if (!state->inUse) {
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
r->_errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make sure there is another file to report on
|
||||
if (! state->validEntry) {
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the filename
|
||||
strncpy (filename, state->currentEntry.filename, NAME_MAX);
|
||||
// Get the stats, if requested
|
||||
if (filestat != NULL) {
|
||||
_FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat);
|
||||
}
|
||||
|
||||
// Look for the next entry for use next time
|
||||
state->validEntry =
|
||||
_FAT_directory_getNextEntry (state->partition, &(state->currentEntry));
|
||||
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) {
|
||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||
|
||||
// We are no longer using this entry
|
||||
_FAT_lock(&state->partition->lock);
|
||||
state->inUse = false;
|
||||
_FAT_unlock(&state->partition->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
75
arm9/src/lib/libfat/source/fatdir.h
Normal file
75
arm9/src/lib/libfat/source/fatdir.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
fatdir.h
|
||||
|
||||
Functions used by the newlib disc stubs to interface with
|
||||
this library
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FATDIR_H
|
||||
#define _FATDIR_H
|
||||
|
||||
#include <sys/reent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/iosupport.h>
|
||||
#include "common.h"
|
||||
#include "directory.h"
|
||||
|
||||
typedef struct {
|
||||
PARTITION* partition;
|
||||
DIR_ENTRY currentEntry;
|
||||
uint32_t startCluster;
|
||||
bool inUse;
|
||||
bool validEntry;
|
||||
} DIR_STATE_STRUCT;
|
||||
|
||||
extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
|
||||
|
||||
extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink);
|
||||
|
||||
extern int _FAT_unlink_r (struct _reent *r, const char *name);
|
||||
|
||||
extern int _FAT_chdir_r (struct _reent *r, const char *name);
|
||||
|
||||
extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||
|
||||
extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode);
|
||||
|
||||
extern int _FAT_rmdir_r (struct _reent *r, const char *path);
|
||||
|
||||
extern int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf);
|
||||
|
||||
/*
|
||||
Directory iterator functions
|
||||
*/
|
||||
extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||
extern int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState);
|
||||
extern int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||
extern int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState);
|
||||
|
||||
|
||||
#endif // _FATDIR_H
|
||||
1218
arm9/src/lib/libfat/source/fatfile.c
Normal file
1218
arm9/src/lib/libfat/source/fatfile.c
Normal file
File diff suppressed because it is too large
Load Diff
103
arm9/src/lib/libfat/source/fatfile.h
Normal file
103
arm9/src/lib/libfat/source/fatfile.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
fatfile.h
|
||||
|
||||
Functions used by the newlib disc stubs to interface with
|
||||
this library
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FATFILE_H
|
||||
#define _FATFILE_H
|
||||
|
||||
#include <sys/reent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "partition.h"
|
||||
#include "directory.h"
|
||||
|
||||
#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B
|
||||
|
||||
typedef struct {
|
||||
u32 cluster;
|
||||
sec_t sector;
|
||||
s32 byte;
|
||||
} FILE_POSITION;
|
||||
|
||||
struct _FILE_STRUCT;
|
||||
|
||||
struct _FILE_STRUCT {
|
||||
uint32_t filesize;
|
||||
uint32_t startCluster;
|
||||
uint32_t currentPosition;
|
||||
FILE_POSITION rwPosition;
|
||||
FILE_POSITION appendPosition;
|
||||
DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
||||
DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry
|
||||
PARTITION* partition;
|
||||
struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files
|
||||
struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files
|
||||
bool read;
|
||||
bool write;
|
||||
bool append;
|
||||
bool inUse;
|
||||
bool modified;
|
||||
};
|
||||
|
||||
typedef struct _FILE_STRUCT FILE_STRUCT;
|
||||
|
||||
int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||
|
||||
int _FAT_close_r (struct _reent *r, void *fd);
|
||||
|
||||
ssize_t _FAT_write_r (struct _reent *r,void *fd, const char *ptr, size_t len);
|
||||
|
||||
ssize_t _FAT_read_r (struct _reent *r, void *fd, char *ptr, size_t len);
|
||||
|
||||
off_t _FAT_seek_r (struct _reent *r, void *fd, off_t pos, int dir);
|
||||
|
||||
int _FAT_fstat_r (struct _reent *r, void *fd, struct stat *st);
|
||||
|
||||
int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
|
||||
|
||||
int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink);
|
||||
|
||||
int _FAT_chdir_r (struct _reent *r, const char *name);
|
||||
|
||||
int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||
|
||||
int _FAT_ftruncate_r (struct _reent *r, void *fd, off_t len);
|
||||
|
||||
int _FAT_fsync_r (struct _reent *r, void *fd);
|
||||
|
||||
/*
|
||||
Synchronizes the file data to disc.
|
||||
Does no locking of its own -- lock the partition before calling.
|
||||
Returns 0 on success, an error code on failure.
|
||||
*/
|
||||
extern int _FAT_syncToDisc (FILE_STRUCT* file);
|
||||
|
||||
#endif // _FATFILE_H
|
||||
393
arm9/src/lib/libfat/source/file_allocation_table.c
Normal file
393
arm9/src/lib/libfat/source/file_allocation_table.c
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
file_allocation_table.c
|
||||
Reading, writing and manipulation of the FAT structure on
|
||||
a FAT partition
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "file_allocation_table.h"
|
||||
#include "partition.h"
|
||||
#include "mem_allocate.h"
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
Gets the cluster linked from input cluster
|
||||
*/
|
||||
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster)
|
||||
{
|
||||
uint32_t nextCluster = CLUSTER_FREE;
|
||||
sec_t sector;
|
||||
int offset;
|
||||
|
||||
if (cluster == CLUSTER_FREE) {
|
||||
return CLUSTER_FREE;
|
||||
}
|
||||
|
||||
switch (partition->filesysType)
|
||||
{
|
||||
case FS_UNKNOWN:
|
||||
return CLUSTER_ERROR;
|
||||
break;
|
||||
|
||||
case FS_FAT12:
|
||||
{
|
||||
u32 nextCluster_h;
|
||||
sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector);
|
||||
offset = ((cluster * 3) / 2) % partition->bytesPerSector;
|
||||
|
||||
|
||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8));
|
||||
|
||||
offset++;
|
||||
|
||||
if (offset >= partition->bytesPerSector) {
|
||||
offset = 0;
|
||||
sector++;
|
||||
}
|
||||
nextCluster_h = 0;
|
||||
|
||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8));
|
||||
nextCluster |= (nextCluster_h << 8);
|
||||
|
||||
if (cluster & 0x01) {
|
||||
nextCluster = nextCluster >> 4;
|
||||
} else {
|
||||
nextCluster &= 0x0FFF;
|
||||
}
|
||||
|
||||
if (nextCluster >= 0x0FF7)
|
||||
{
|
||||
nextCluster = CLUSTER_EOF;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case FS_FAT16:
|
||||
sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector);
|
||||
offset = (cluster % (partition->bytesPerSector >> 1)) << 1;
|
||||
|
||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16));
|
||||
|
||||
if (nextCluster >= 0xFFF7) {
|
||||
nextCluster = CLUSTER_EOF;
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_FAT32:
|
||||
sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector);
|
||||
offset = (cluster % (partition->bytesPerSector >> 2)) << 2;
|
||||
|
||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32));
|
||||
|
||||
if (nextCluster >= 0x0FFFFFF7) {
|
||||
nextCluster = CLUSTER_EOF;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return CLUSTER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return nextCluster;
|
||||
}
|
||||
|
||||
/*
|
||||
writes value into the correct offset within a partition's FAT, based
|
||||
on the cluster number.
|
||||
*/
|
||||
static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint32_t value) {
|
||||
sec_t sector;
|
||||
int offset;
|
||||
uint32_t oldValue;
|
||||
|
||||
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (partition->filesysType)
|
||||
{
|
||||
case FS_UNKNOWN:
|
||||
return false;
|
||||
break;
|
||||
|
||||
case FS_FAT12:
|
||||
sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector);
|
||||
offset = ((cluster * 3) / 2) % partition->bytesPerSector;
|
||||
|
||||
if (cluster & 0x01) {
|
||||
|
||||
_FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8));
|
||||
|
||||
value = (value << 4) | (oldValue & 0x0F);
|
||||
|
||||
_FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8));
|
||||
|
||||
offset++;
|
||||
if (offset >= partition->bytesPerSector) {
|
||||
offset = 0;
|
||||
sector++;
|
||||
}
|
||||
|
||||
_FAT_cache_writeLittleEndianValue (partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8));
|
||||
|
||||
} else {
|
||||
|
||||
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8));
|
||||
|
||||
offset++;
|
||||
if (offset >= partition->bytesPerSector) {
|
||||
offset = 0;
|
||||
sector++;
|
||||
}
|
||||
|
||||
_FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8));
|
||||
|
||||
value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
|
||||
|
||||
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case FS_FAT16:
|
||||
sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector);
|
||||
offset = (cluster % (partition->bytesPerSector >> 1)) << 1;
|
||||
|
||||
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16));
|
||||
|
||||
break;
|
||||
|
||||
case FS_FAT32:
|
||||
sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector);
|
||||
offset = (cluster % (partition->bytesPerSector >> 2)) << 2;
|
||||
|
||||
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u32));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
gets the first available free cluster, sets it
|
||||
to end of file, links the input cluster to it then returns the
|
||||
cluster number
|
||||
If an error occurs, return CLUSTER_ERROR
|
||||
-----------------------------------------------------------------*/
|
||||
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) {
|
||||
uint32_t firstFree;
|
||||
uint32_t curLink;
|
||||
uint32_t lastCluster;
|
||||
bool loopedAroundFAT = false;
|
||||
|
||||
lastCluster = partition->fat.lastCluster;
|
||||
|
||||
if (cluster > lastCluster) {
|
||||
return CLUSTER_ERROR;
|
||||
}
|
||||
|
||||
// Check if the cluster already has a link, and return it if so
|
||||
curLink = _FAT_fat_nextCluster(partition, cluster);
|
||||
if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) {
|
||||
return curLink; // Return the current link - don't allocate a new one
|
||||
}
|
||||
|
||||
// Get a free cluster
|
||||
firstFree = partition->fat.firstFree;
|
||||
// Start at first valid cluster
|
||||
if (firstFree < CLUSTER_FIRST) {
|
||||
firstFree = CLUSTER_FIRST;
|
||||
}
|
||||
|
||||
// Search until a free cluster is found
|
||||
while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) {
|
||||
firstFree++;
|
||||
if (firstFree > lastCluster) {
|
||||
if (loopedAroundFAT) {
|
||||
// If couldn't get a free cluster then return an error
|
||||
partition->fat.firstFree = firstFree;
|
||||
return CLUSTER_ERROR;
|
||||
} else {
|
||||
// Try looping back to the beginning of the FAT
|
||||
// This was suggested by loopy
|
||||
firstFree = CLUSTER_FIRST;
|
||||
loopedAroundFAT = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
partition->fat.firstFree = firstFree;
|
||||
if(partition->fat.numberFreeCluster)
|
||||
partition->fat.numberFreeCluster--;
|
||||
partition->fat.numberLastAllocCluster = firstFree;
|
||||
|
||||
if ((cluster >= CLUSTER_FIRST) && (cluster <= lastCluster))
|
||||
{
|
||||
// Update the linked from FAT entry
|
||||
_FAT_fat_writeFatEntry (partition, cluster, firstFree);
|
||||
}
|
||||
// Create the linked to FAT entry
|
||||
_FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF);
|
||||
|
||||
return firstFree;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
gets the first available free cluster, sets it
|
||||
to end of file, links the input cluster to it, clears the new
|
||||
cluster to 0 valued bytes, then returns the cluster number
|
||||
If an error occurs, return CLUSTER_ERROR
|
||||
-----------------------------------------------------------------*/
|
||||
uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) {
|
||||
uint32_t newCluster;
|
||||
uint32_t i;
|
||||
uint8_t *emptySector;
|
||||
|
||||
// Link the cluster
|
||||
newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
|
||||
|
||||
if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) {
|
||||
return CLUSTER_ERROR;
|
||||
}
|
||||
|
||||
emptySector = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector);
|
||||
|
||||
// Clear all the sectors within the cluster
|
||||
memset (emptySector, 0, partition->bytesPerSector);
|
||||
for (i = 0; i < partition->sectorsPerCluster; i++) {
|
||||
_FAT_cache_writeSectors (partition->cache,
|
||||
_FAT_fat_clusterToSector (partition, newCluster) + i,
|
||||
1, emptySector);
|
||||
}
|
||||
|
||||
_FAT_mem_free(emptySector);
|
||||
|
||||
return newCluster;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
_FAT_fat_clearLinks
|
||||
frees any cluster used by a file
|
||||
-----------------------------------------------------------------*/
|
||||
bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster) {
|
||||
uint32_t nextCluster;
|
||||
|
||||
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
||||
return false;
|
||||
|
||||
// If this clears up more space in the FAT before the current free pointer, move it backwards
|
||||
if (cluster < partition->fat.firstFree) {
|
||||
partition->fat.firstFree = cluster;
|
||||
}
|
||||
|
||||
while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE) && (cluster != CLUSTER_ERROR)) {
|
||||
// Store next cluster before erasing the link
|
||||
nextCluster = _FAT_fat_nextCluster (partition, cluster);
|
||||
|
||||
// Erase the link
|
||||
_FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE);
|
||||
|
||||
if(partition->fat.numberFreeCluster < (partition->numberOfSectors/partition->sectorsPerCluster))
|
||||
partition->fat.numberFreeCluster++;
|
||||
// Move onto next cluster
|
||||
cluster = nextCluster;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
_FAT_fat_trimChain
|
||||
Drop all clusters past the chainLength.
|
||||
If chainLength is 0, all clusters are dropped.
|
||||
If chainLength is 1, the first cluster is kept and the rest are
|
||||
dropped, and so on.
|
||||
Return the last cluster left in the chain.
|
||||
-----------------------------------------------------------------*/
|
||||
uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) {
|
||||
uint32_t nextCluster;
|
||||
|
||||
if (chainLength == 0) {
|
||||
// Drop the entire chain
|
||||
_FAT_fat_clearLinks (partition, startCluster);
|
||||
return CLUSTER_FREE;
|
||||
} else {
|
||||
// Find the last cluster in the chain, and the one after it
|
||||
chainLength--;
|
||||
nextCluster = _FAT_fat_nextCluster (partition, startCluster);
|
||||
while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
|
||||
chainLength--;
|
||||
startCluster = nextCluster;
|
||||
nextCluster = _FAT_fat_nextCluster (partition, startCluster);
|
||||
}
|
||||
|
||||
// Drop all clusters after the last in the chain
|
||||
if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) {
|
||||
_FAT_fat_clearLinks (partition, nextCluster);
|
||||
}
|
||||
|
||||
// Mark the last cluster in the chain as the end of the file
|
||||
_FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF);
|
||||
|
||||
return startCluster;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
_FAT_fat_lastCluster
|
||||
Trace the cluster links until the last one is found
|
||||
-----------------------------------------------------------------*/
|
||||
uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) {
|
||||
while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
|
||||
cluster = _FAT_fat_nextCluster(partition, cluster);
|
||||
}
|
||||
return cluster;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
_FAT_fat_freeClusterCount
|
||||
Return the number of free clusters available
|
||||
-----------------------------------------------------------------*/
|
||||
unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) {
|
||||
unsigned int count = 0;
|
||||
uint32_t curCluster;
|
||||
|
||||
for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) {
|
||||
if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
70
arm9/src/lib/libfat/source/file_allocation_table.h
Normal file
70
arm9/src/lib/libfat/source/file_allocation_table.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
file_allocation_table.h
|
||||
Reading, writing and manipulation of the FAT structure on
|
||||
a FAT partition
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FAT_H
|
||||
#define _FAT_H
|
||||
|
||||
#include "common.h"
|
||||
#include "partition.h"
|
||||
|
||||
#define CLUSTER_EOF_16 0xFFFF
|
||||
#define CLUSTER_EOF 0x0FFFFFFF
|
||||
#define CLUSTER_FREE 0x00000000
|
||||
#define CLUSTER_ROOT 0x00000000
|
||||
#define CLUSTER_FIRST 0x00000002
|
||||
#define CLUSTER_ERROR 0xFFFFFFFF
|
||||
|
||||
#define CLUSTERS_PER_FAT12 4085
|
||||
#define CLUSTERS_PER_FAT16 65525
|
||||
|
||||
|
||||
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster);
|
||||
|
||||
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster);
|
||||
uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster);
|
||||
|
||||
bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster);
|
||||
|
||||
uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength);
|
||||
|
||||
uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster);
|
||||
|
||||
unsigned int _FAT_fat_freeClusterCount (PARTITION* partition);
|
||||
|
||||
static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) {
|
||||
return (cluster >= CLUSTER_FIRST) ?
|
||||
((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart :
|
||||
partition->rootDirStart;
|
||||
}
|
||||
|
||||
static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) {
|
||||
return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */);
|
||||
}
|
||||
|
||||
#endif // _FAT_H
|
||||
107
arm9/src/lib/libfat/source/filetime.c
Normal file
107
arm9/src/lib/libfat/source/filetime.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
filetime.c
|
||||
Conversion of file time and date values to various other types
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <time.h>
|
||||
#include "filetime.h"
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_HOUR 23
|
||||
#define MAX_MINUTE 59
|
||||
#define MAX_SECOND 59
|
||||
|
||||
#define MAX_MONTH 11
|
||||
#define MIN_MONTH 0
|
||||
#define MAX_DAY 31
|
||||
#define MIN_DAY 1
|
||||
|
||||
uint16_t _FAT_filetime_getTimeFromRTC (void) {
|
||||
#ifdef USE_RTC_TIME
|
||||
struct tm timeParts;
|
||||
time_t epochTime;
|
||||
|
||||
if (time(&epochTime) == (time_t)-1) {
|
||||
return 0;
|
||||
}
|
||||
localtime_r(&epochTime, &timeParts);
|
||||
|
||||
// Check that the values are all in range.
|
||||
// If they are not, return 0 (no timestamp)
|
||||
if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0;
|
||||
if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0;
|
||||
if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0;
|
||||
|
||||
return (
|
||||
((timeParts.tm_hour & 0x1F) << 11) |
|
||||
((timeParts.tm_min & 0x3F) << 5) |
|
||||
((timeParts.tm_sec >> 1) & 0x1F)
|
||||
);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint16_t _FAT_filetime_getDateFromRTC (void) {
|
||||
#ifdef USE_RTC_TIME
|
||||
struct tm timeParts;
|
||||
time_t epochTime;
|
||||
|
||||
if (time(&epochTime) == (time_t)-1) {
|
||||
return 0;
|
||||
}
|
||||
localtime_r(&epochTime, &timeParts);
|
||||
|
||||
if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0;
|
||||
if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0;
|
||||
|
||||
return (
|
||||
(((timeParts.tm_year - 80) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year)
|
||||
(((timeParts.tm_mon + 1) & 0xF) << 5) |
|
||||
(timeParts.tm_mday & 0x1F)
|
||||
);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) {
|
||||
struct tm timeParts;
|
||||
|
||||
timeParts.tm_hour = t >> 11;
|
||||
timeParts.tm_min = (t >> 5) & 0x3F;
|
||||
timeParts.tm_sec = (t & 0x1F) << 1;
|
||||
|
||||
timeParts.tm_mday = d & 0x1F;
|
||||
timeParts.tm_mon = ((d >> 5) & 0x0F) - 1;
|
||||
timeParts.tm_year = (d >> 9) + 80;
|
||||
|
||||
timeParts.tm_isdst = 0;
|
||||
|
||||
return mktime(&timeParts);
|
||||
}
|
||||
41
arm9/src/lib/libfat/source/filetime.h
Normal file
41
arm9/src/lib/libfat/source/filetime.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
filetime.h
|
||||
Conversion of file time and date values to various other types
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FILETIME_H
|
||||
#define _FILETIME_H
|
||||
|
||||
#include "common.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
uint16_t _FAT_filetime_getTimeFromRTC (void);
|
||||
uint16_t _FAT_filetime_getDateFromRTC (void);
|
||||
|
||||
time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d);
|
||||
|
||||
|
||||
#endif // _FILETIME_H
|
||||
259
arm9/src/lib/libfat/source/libfat.c
Normal file
259
arm9/src/lib/libfat/source/libfat.c
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
libfat.c
|
||||
Simple functionality for startup, mounting and unmounting of FAT-based devices.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/iosupport.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "partition.h"
|
||||
#include "fatfile.h"
|
||||
#include "fatdir.h"
|
||||
#include "lock.h"
|
||||
#include "mem_allocate.h"
|
||||
#include "disc.h"
|
||||
|
||||
static const devoptab_t dotab_fat = {
|
||||
"fat",
|
||||
sizeof (FILE_STRUCT),
|
||||
_FAT_open_r,
|
||||
_FAT_close_r,
|
||||
_FAT_write_r,
|
||||
_FAT_read_r,
|
||||
_FAT_seek_r,
|
||||
_FAT_fstat_r,
|
||||
_FAT_stat_r,
|
||||
_FAT_link_r,
|
||||
_FAT_unlink_r,
|
||||
_FAT_chdir_r,
|
||||
_FAT_rename_r,
|
||||
_FAT_mkdir_r,
|
||||
sizeof (DIR_STATE_STRUCT),
|
||||
_FAT_diropen_r,
|
||||
_FAT_dirreset_r,
|
||||
_FAT_dirnext_r,
|
||||
_FAT_dirclose_r,
|
||||
_FAT_statvfs_r,
|
||||
_FAT_ftruncate_r,
|
||||
_FAT_fsync_r,
|
||||
NULL, /* Device data */
|
||||
NULL, // chmod_r
|
||||
NULL, // fchmod_r
|
||||
_FAT_rmdir_r,
|
||||
_FAT_stat_r, // This is lstat, but we don't support symlinks
|
||||
};
|
||||
|
||||
bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage, bool isNand) {
|
||||
PARTITION* partition;
|
||||
devoptab_t* devops;
|
||||
char* nameCopy;
|
||||
|
||||
if(!name || strlen(name) > 8 || !interface)
|
||||
return false;
|
||||
|
||||
/*
|
||||
if(!isNand) {
|
||||
if(!interface->startup())
|
||||
return false;
|
||||
}*/
|
||||
|
||||
if(!interface->isInserted())
|
||||
return false;
|
||||
|
||||
char devname[10];
|
||||
strcpy(devname, name);
|
||||
strcat(devname, ":");
|
||||
if(FindDevice(devname) >= 0)
|
||||
return true;
|
||||
|
||||
devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1);
|
||||
if (!devops) {
|
||||
return false;
|
||||
}
|
||||
// Use the space allocated at the end of the devoptab struct for storing the name
|
||||
nameCopy = (char*)(devops+1);
|
||||
|
||||
// Initialize the file system
|
||||
partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector);
|
||||
if (!partition) {
|
||||
_FAT_mem_free (devops);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add an entry for this device to the devoptab table
|
||||
memcpy (devops, &dotab_fat, sizeof(dotab_fat));
|
||||
strcpy (nameCopy, name);
|
||||
devops->name = nameCopy;
|
||||
devops->deviceData = partition;
|
||||
|
||||
AddDevice (devops);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fatMountSimple (const char* name, const DISC_INTERFACE* interface, bool isNand) {
|
||||
return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE, isNand);
|
||||
}
|
||||
|
||||
void fatUnmount (const char* name) {
|
||||
devoptab_t *devops;
|
||||
PARTITION* partition;
|
||||
|
||||
if(!name)
|
||||
return;
|
||||
|
||||
devops = (devoptab_t*)GetDeviceOpTab (name);
|
||||
if (!devops) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform a quick check to make sure we're dealing with a libfat controlled device
|
||||
if (devops->open_r != dotab_fat.open_r) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RemoveDevice (name) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
partition = (PARTITION*)devops->deviceData;
|
||||
_FAT_partition_destructor (partition);
|
||||
_FAT_mem_free (devops);
|
||||
}
|
||||
|
||||
bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) {
|
||||
int i;
|
||||
int defaultDevice = -1;
|
||||
const DISC_INTERFACE *disc;
|
||||
|
||||
for (i = 0;
|
||||
_FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
|
||||
i++)
|
||||
{
|
||||
disc = _FAT_disc_interfaces[i].getInterface();
|
||||
if (!disc) {
|
||||
continue;
|
||||
}
|
||||
if (fatMount(_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE, false)) {
|
||||
// The first device to successfully mount is set as the default
|
||||
if (defaultDevice < 0) {
|
||||
defaultDevice = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultDevice < 0) {
|
||||
// None of our devices mounted
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setAsDefaultDevice) {
|
||||
char filePath[PATH_MAX];
|
||||
strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name);
|
||||
strcat (filePath, ":/");
|
||||
#ifdef ARGV_MAGIC
|
||||
if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) {
|
||||
// Check the app's path against each of our mounted devices, to see
|
||||
// if we can support it. If so, change to that path.
|
||||
for (i = 0;
|
||||
_FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
|
||||
i++)
|
||||
{
|
||||
if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name,
|
||||
strlen(_FAT_disc_interfaces[i].name)))
|
||||
{
|
||||
char *lastSlash;
|
||||
strcpy(filePath, __system_argv->argv[0]);
|
||||
lastSlash = strrchr( filePath, '/' );
|
||||
|
||||
if ( NULL != lastSlash) {
|
||||
if ( *(lastSlash - 1) == ':') lastSlash++;
|
||||
*lastSlash = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
chdir (filePath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fatInitDefault (void) {
|
||||
return fatInit (DEFAULT_CACHE_PAGES, true);
|
||||
}
|
||||
|
||||
void fatGetVolumeLabel (const char* name, char *label) {
|
||||
devoptab_t *devops;
|
||||
PARTITION* partition;
|
||||
char *buf;
|
||||
int namelen,i;
|
||||
|
||||
if(!name || !label)
|
||||
return;
|
||||
|
||||
namelen = strlen(name);
|
||||
buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2);
|
||||
strcpy(buf,name);
|
||||
|
||||
if (name[namelen-1] == '/') {
|
||||
buf[namelen-1]='\0';
|
||||
namelen--;
|
||||
}
|
||||
|
||||
if (name[namelen-1] != ':') {
|
||||
buf[namelen]=':';
|
||||
buf[namelen+1]='\0';
|
||||
}
|
||||
|
||||
devops = (devoptab_t*)GetDeviceOpTab(buf);
|
||||
|
||||
for(i=0;buf[i]!='\0' && buf[i]!=':';i++);
|
||||
if (!devops || strncasecmp(buf,devops->name,i)) {
|
||||
_FAT_mem_free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
_FAT_mem_free(buf);
|
||||
|
||||
// Perform a quick check to make sure we're dealing with a libfat controlled device
|
||||
if (devops->open_r != dotab_fat.open_r) {
|
||||
return;
|
||||
}
|
||||
|
||||
partition = (PARTITION*)devops->deviceData;
|
||||
|
||||
if(!_FAT_directory_getVolumeLabel(partition, label)) {
|
||||
strncpy(label,partition->label,11);
|
||||
label[11]='\0';
|
||||
}
|
||||
if(!strncmp(label, "NO NAME", 7)) label[0]='\0';
|
||||
}
|
||||
29
arm9/src/lib/libfat/source/lock.c
Normal file
29
arm9/src/lib/libfat/source/lock.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "common.h"
|
||||
|
||||
#ifndef USE_LWP_LOCK
|
||||
|
||||
#ifndef mutex_t
|
||||
typedef int mutex_t;
|
||||
#endif
|
||||
|
||||
void __attribute__ ((weak)) _FAT_lock_init(mutex_t *mutex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void __attribute__ ((weak)) _FAT_lock_deinit(mutex_t *mutex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void __attribute__ ((weak)) _FAT_lock(mutex_t *mutex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void __attribute__ ((weak)) _FAT_unlock(mutex_t *mutex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // USE_LWP_LOCK
|
||||
72
arm9/src/lib/libfat/source/lock.h
Normal file
72
arm9/src/lib/libfat/source/lock.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
lock.h
|
||||
|
||||
Copyright (c) 2008 Sven Peter <svpe@gmx.net>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _LOCK_H
|
||||
#define _LOCK_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef USE_LWP_LOCK
|
||||
|
||||
static inline void _FAT_lock_init(mutex_t *mutex)
|
||||
{
|
||||
LWP_MutexInit(mutex, false);
|
||||
}
|
||||
|
||||
static inline void _FAT_lock_deinit(mutex_t *mutex)
|
||||
{
|
||||
LWP_MutexDestroy(*mutex);
|
||||
}
|
||||
|
||||
static inline void _FAT_lock(mutex_t *mutex)
|
||||
{
|
||||
LWP_MutexLock(*mutex);
|
||||
}
|
||||
|
||||
static inline void _FAT_unlock(mutex_t *mutex)
|
||||
{
|
||||
LWP_MutexUnlock(*mutex);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// We still need a blank lock type
|
||||
#ifndef mutex_t
|
||||
typedef int mutex_t;
|
||||
#endif
|
||||
|
||||
void _FAT_lock_init(mutex_t *mutex);
|
||||
void _FAT_lock_deinit(mutex_t *mutex);
|
||||
void _FAT_lock(mutex_t *mutex);
|
||||
void _FAT_unlock(mutex_t *mutex);
|
||||
|
||||
#endif // USE_LWP_LOCK
|
||||
|
||||
|
||||
#endif // _LOCK_H
|
||||
|
||||
52
arm9/src/lib/libfat/source/mem_allocate.h
Normal file
52
arm9/src/lib/libfat/source/mem_allocate.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
mem_allocate.h
|
||||
Memory allocation and destruction calls
|
||||
Replace these calls with custom allocators if
|
||||
malloc is unavailable
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _MEM_ALLOCATE_H
|
||||
#define _MEM_ALLOCATE_H
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
static inline void* _FAT_mem_allocate (size_t size) {
|
||||
return malloc (size);
|
||||
}
|
||||
|
||||
static inline void* _FAT_mem_align (size_t size) {
|
||||
#ifdef __wii__
|
||||
return memalign (32, size);
|
||||
#else
|
||||
return malloc (size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _FAT_mem_free (void* mem) {
|
||||
free (mem);
|
||||
}
|
||||
|
||||
#endif // _MEM_ALLOCATE_H
|
||||
455
arm9/src/lib/libfat/source/partition.c
Normal file
455
arm9/src/lib/libfat/source/partition.c
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
partition.c
|
||||
Functions for mounting and dismounting partitions
|
||||
on various block devices.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "partition.h"
|
||||
#include "bit_ops.h"
|
||||
#include "file_allocation_table.h"
|
||||
#include "directory.h"
|
||||
#include "mem_allocate.h"
|
||||
#include "fatfile.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/iosupport.h>
|
||||
|
||||
/*
|
||||
Data offsets
|
||||
*/
|
||||
|
||||
// BIOS Parameter Block offsets
|
||||
enum BPB {
|
||||
BPB_jmpBoot = 0x00,
|
||||
BPB_OEMName = 0x03,
|
||||
// BIOS Parameter Block
|
||||
BPB_bytesPerSector = 0x0B,
|
||||
BPB_sectorsPerCluster = 0x0D,
|
||||
BPB_reservedSectors = 0x0E,
|
||||
BPB_numFATs = 0x10,
|
||||
BPB_rootEntries = 0x11,
|
||||
BPB_numSectorsSmall = 0x13,
|
||||
BPB_mediaDesc = 0x15,
|
||||
BPB_sectorsPerFAT = 0x16,
|
||||
BPB_sectorsPerTrk = 0x18,
|
||||
BPB_numHeads = 0x1A,
|
||||
BPB_numHiddenSectors = 0x1C,
|
||||
BPB_numSectors = 0x20,
|
||||
// Ext BIOS Parameter Block for FAT16
|
||||
BPB_FAT16_driveNumber = 0x24,
|
||||
BPB_FAT16_reserved1 = 0x25,
|
||||
BPB_FAT16_extBootSig = 0x26,
|
||||
BPB_FAT16_volumeID = 0x27,
|
||||
BPB_FAT16_volumeLabel = 0x2B,
|
||||
BPB_FAT16_fileSysType = 0x36,
|
||||
// Bootcode
|
||||
BPB_FAT16_bootCode = 0x3E,
|
||||
// FAT32 extended block
|
||||
BPB_FAT32_sectorsPerFAT32 = 0x24,
|
||||
BPB_FAT32_extFlags = 0x28,
|
||||
BPB_FAT32_fsVer = 0x2A,
|
||||
BPB_FAT32_rootClus = 0x2C,
|
||||
BPB_FAT32_fsInfo = 0x30,
|
||||
BPB_FAT32_bkBootSec = 0x32,
|
||||
// Ext BIOS Parameter Block for FAT32
|
||||
BPB_FAT32_driveNumber = 0x40,
|
||||
BPB_FAT32_reserved1 = 0x41,
|
||||
BPB_FAT32_extBootSig = 0x42,
|
||||
BPB_FAT32_volumeID = 0x43,
|
||||
BPB_FAT32_volumeLabel = 0x47,
|
||||
BPB_FAT32_fileSysType = 0x52,
|
||||
// Bootcode
|
||||
BPB_FAT32_bootCode = 0x5A,
|
||||
BPB_bootSig_55 = 0x1FE,
|
||||
BPB_bootSig_AA = 0x1FF
|
||||
};
|
||||
|
||||
// File system information block offsets
|
||||
enum FSIB
|
||||
{
|
||||
FSIB_SIG1 = 0x00,
|
||||
FSIB_SIG2 = 0x1e4,
|
||||
FSIB_numberOfFreeCluster = 0x1e8,
|
||||
FSIB_numberLastAllocCluster = 0x1ec,
|
||||
FSIB_bootSig_55 = 0x1FE,
|
||||
FSIB_bootSig_AA = 0x1FF
|
||||
};
|
||||
|
||||
static const char FAT_SIG[3] = {'F', 'A', 'T'};
|
||||
static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'};
|
||||
static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'};
|
||||
static const char FS_TWL_SIG[8] = { 0xe9, 0x00, 0x00, 0x54, 0x57, 0x4c, 0x20, 0x20 };
|
||||
|
||||
static bool isValidMBR(uint8_t *sectorBuffer) {
|
||||
return (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
|
||||
!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
|
||||
!memcmp(sectorBuffer, FS_TWL_SIG, sizeof(FS_TWL_SIG)));
|
||||
|
||||
}
|
||||
|
||||
sec_t FindFirstValidPartition_buf(const DISC_INTERFACE* disc, uint8_t *sectorBuffer)
|
||||
{
|
||||
uint8_t part_table[16*4];
|
||||
uint8_t *ptr;
|
||||
int i;
|
||||
|
||||
// Read first sector of disc
|
||||
if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(part_table,sectorBuffer+0x1BE,16*4);
|
||||
ptr = part_table;
|
||||
|
||||
for(i=0;i<4;i++,ptr+=16) {
|
||||
sec_t part_lba = u8array_to_u32(ptr, 0x8);
|
||||
|
||||
if (isValidMBR(sectorBuffer))
|
||||
{
|
||||
return part_lba;
|
||||
}
|
||||
|
||||
if(ptr[4]==0) continue;
|
||||
|
||||
if(ptr[4]==0x0F) {
|
||||
sec_t part_lba2=part_lba;
|
||||
sec_t next_lba2=0;
|
||||
int n;
|
||||
|
||||
for(n=0;n<8;n++) // max 8 logic partitions
|
||||
{
|
||||
if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0;
|
||||
|
||||
part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ;
|
||||
next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6);
|
||||
|
||||
if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0;
|
||||
|
||||
if (isValidMBR(sectorBuffer))
|
||||
{
|
||||
return part_lba2;
|
||||
}
|
||||
|
||||
if(next_lba2==0) break;
|
||||
}
|
||||
} else {
|
||||
if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0;
|
||||
|
||||
|
||||
if (isValidMBR(sectorBuffer))
|
||||
{
|
||||
return part_lba;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sec_t FindFirstValidPartition(const DISC_INTERFACE* disc)
|
||||
{
|
||||
uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE);
|
||||
if (!sectorBuffer) return 0;
|
||||
sec_t ret = FindFirstValidPartition_buf(disc, sectorBuffer);
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PARTITION* _FAT_partition_constructor_buf (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector, uint8_t *sectorBuffer)
|
||||
{
|
||||
PARTITION* partition;
|
||||
|
||||
// Read first sector of disc
|
||||
if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Make sure it is a valid MBR or boot sector
|
||||
if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (startSector != 0) {
|
||||
// We're told where to start the partition, so just accept it
|
||||
} else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||
// Check if there is a FAT string, which indicates this is a boot sector
|
||||
startSector = 0;
|
||||
} else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||
// Check for FAT32
|
||||
startSector = 0;
|
||||
} else {
|
||||
startSector = FindFirstValidPartition_buf(disc, sectorBuffer);
|
||||
if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidMBR(sectorBuffer))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
|
||||
if (partition == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Init the partition lock
|
||||
_FAT_lock_init(&partition->lock);
|
||||
|
||||
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
||||
strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11);
|
||||
else
|
||||
strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11);
|
||||
partition->label[11] = '\0';
|
||||
|
||||
// Set partition's disc interface
|
||||
partition->disc = disc;
|
||||
|
||||
// Store required information about the file system
|
||||
partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
|
||||
if (partition->fat.sectorsPerFat == 0) {
|
||||
partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
|
||||
}
|
||||
|
||||
partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
|
||||
if (partition->numberOfSectors == 0) {
|
||||
partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
|
||||
}
|
||||
|
||||
partition->bytesPerSector = u8array_to_u16(sectorBuffer, BPB_bytesPerSector);
|
||||
if(partition->bytesPerSector < MIN_SECTOR_SIZE || partition->bytesPerSector > MAX_SECTOR_SIZE) {
|
||||
// Unsupported sector size
|
||||
_FAT_mem_free(partition);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster];
|
||||
partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
|
||||
partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
|
||||
|
||||
partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
|
||||
partition->dataStart = partition->rootDirStart +
|
||||
(( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
|
||||
|
||||
partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
|
||||
|
||||
//FS info sector
|
||||
partition->fsInfoSector = startSector + (u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) ? u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) : 1);
|
||||
|
||||
// Store info about FAT
|
||||
uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
|
||||
partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1;
|
||||
partition->fat.firstFree = CLUSTER_FIRST;
|
||||
partition->fat.numberFreeCluster = 0;
|
||||
partition->fat.numberLastAllocCluster = 0;
|
||||
|
||||
if (clusterCount < CLUSTERS_PER_FAT12) {
|
||||
partition->filesysType = FS_FAT12; // FAT12 volume
|
||||
} else if (clusterCount < CLUSTERS_PER_FAT16) {
|
||||
partition->filesysType = FS_FAT16; // FAT16 volume
|
||||
} else {
|
||||
partition->filesysType = FS_FAT32; // FAT32 volume
|
||||
}
|
||||
|
||||
if (partition->filesysType != FS_FAT32) {
|
||||
partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
|
||||
} else {
|
||||
// Set up for the FAT32 way
|
||||
partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
|
||||
// Check if FAT mirroring is enabled
|
||||
if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
|
||||
// Use the active FAT
|
||||
partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a cache to use
|
||||
partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors, partition->bytesPerSector);
|
||||
|
||||
// Set current directory to the root
|
||||
partition->cwdCluster = partition->rootDirCluster;
|
||||
|
||||
// Check if this disc is writable, and set the readOnly property appropriately
|
||||
partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
|
||||
|
||||
// There are currently no open files on this partition
|
||||
partition->openFileCount = 0;
|
||||
partition->firstOpenFile = NULL;
|
||||
|
||||
_FAT_partition_readFSinfo(partition);
|
||||
|
||||
return partition;
|
||||
}
|
||||
|
||||
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector)
|
||||
{
|
||||
uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE);
|
||||
if (!sectorBuffer) return NULL;
|
||||
PARTITION *ret = _FAT_partition_constructor_buf(disc, cacheSize,
|
||||
sectorsPerPage, startSector, sectorBuffer);
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void _FAT_partition_destructor (PARTITION* partition) {
|
||||
FILE_STRUCT* nextFile;
|
||||
|
||||
_FAT_lock(&partition->lock);
|
||||
|
||||
// Synchronize open files
|
||||
nextFile = partition->firstOpenFile;
|
||||
while (nextFile) {
|
||||
_FAT_syncToDisc (nextFile);
|
||||
nextFile = nextFile->nextOpenFile;
|
||||
}
|
||||
|
||||
// Write out the fs info sector
|
||||
_FAT_partition_writeFSinfo(partition);
|
||||
|
||||
// Free memory used by the cache, writing it to disc at the same time
|
||||
_FAT_cache_destructor (partition->cache);
|
||||
|
||||
// Unlock the partition and destroy the lock
|
||||
_FAT_unlock(&partition->lock);
|
||||
_FAT_lock_deinit(&partition->lock);
|
||||
|
||||
// Free memory used by the partition
|
||||
_FAT_mem_free (partition);
|
||||
}
|
||||
|
||||
PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
|
||||
const devoptab_t *devops;
|
||||
|
||||
devops = GetDeviceOpTab (path);
|
||||
|
||||
if (!devops) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (PARTITION*)devops->deviceData;
|
||||
}
|
||||
|
||||
static void _FAT_updateFS_INFO(PARTITION * partition, uint8_t *sectorBuffer) {
|
||||
partition->fat.numberFreeCluster = _FAT_fat_freeClusterCount(partition);
|
||||
u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster);
|
||||
u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster);
|
||||
_FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer);
|
||||
}
|
||||
|
||||
void _FAT_partition_createFSinfo(PARTITION * partition)
|
||||
{
|
||||
if(partition->readOnly || partition->filesysType != FS_FAT32)
|
||||
return;
|
||||
|
||||
uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector);
|
||||
if (!sectorBuffer) return;
|
||||
memset(sectorBuffer, 0, partition->bytesPerSector);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 4; ++i)
|
||||
{
|
||||
sectorBuffer[FSIB_SIG1+i] = FS_INFO_SIG1[i];
|
||||
sectorBuffer[FSIB_SIG2+i] = FS_INFO_SIG2[i];
|
||||
}
|
||||
|
||||
sectorBuffer[FSIB_bootSig_55] = 0x55;
|
||||
sectorBuffer[FSIB_bootSig_AA] = 0xAA;
|
||||
|
||||
_FAT_updateFS_INFO(partition,sectorBuffer);
|
||||
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
}
|
||||
|
||||
void _FAT_partition_readFSinfo(PARTITION * partition)
|
||||
{
|
||||
if(partition->filesysType != FS_FAT32)
|
||||
return;
|
||||
|
||||
uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector);
|
||||
if (!sectorBuffer) return;
|
||||
memset(sectorBuffer, 0, partition->bytesPerSector);
|
||||
// Read first sector of disc
|
||||
if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) {
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 ||
|
||||
memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 ||
|
||||
u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0)
|
||||
{
|
||||
//sector does not yet exist, create one!
|
||||
_FAT_partition_createFSinfo(partition);
|
||||
} else {
|
||||
partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster);
|
||||
if(partition->fat.numberFreeCluster == 0xffffffff) {
|
||||
_FAT_updateFS_INFO(partition,sectorBuffer);
|
||||
partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster);
|
||||
}
|
||||
partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster);
|
||||
}
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
}
|
||||
|
||||
void _FAT_partition_writeFSinfo(PARTITION * partition)
|
||||
{
|
||||
if(partition->filesysType != FS_FAT32)
|
||||
return;
|
||||
|
||||
uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector);
|
||||
if (!sectorBuffer) return;
|
||||
memset(sectorBuffer, 0, partition->bytesPerSector);
|
||||
// Read first sector of disc
|
||||
if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) {
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) {
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster);
|
||||
u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster);
|
||||
|
||||
// Write first sector of disc
|
||||
_FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer);
|
||||
_FAT_mem_free(sectorBuffer);
|
||||
}
|
||||
|
||||
uint32_t* _FAT_getCwdClusterPtr(const char* name) {
|
||||
PARTITION *partition = _FAT_partition_getPartitionFromPath(name);
|
||||
|
||||
if (!partition) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &partition->cwdCluster;
|
||||
}
|
||||
107
arm9/src/lib/libfat/source/partition.h
Normal file
107
arm9/src/lib/libfat/source/partition.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
partition.h
|
||||
Functions for mounting and dismounting partitions
|
||||
on various block devices.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PARTITION_H
|
||||
#define _PARTITION_H
|
||||
|
||||
#include "common.h"
|
||||
#include "cache.h"
|
||||
#include "lock.h"
|
||||
|
||||
#define MIN_SECTOR_SIZE 512
|
||||
#define MAX_SECTOR_SIZE 4096
|
||||
|
||||
// Filesystem type
|
||||
typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
|
||||
|
||||
typedef struct {
|
||||
sec_t fatStart;
|
||||
uint32_t sectorsPerFat;
|
||||
uint32_t lastCluster;
|
||||
uint32_t firstFree;
|
||||
uint32_t numberFreeCluster;
|
||||
uint32_t numberLastAllocCluster;
|
||||
} FAT;
|
||||
|
||||
typedef struct {
|
||||
const DISC_INTERFACE* disc;
|
||||
CACHE* cache;
|
||||
// Info about the partition
|
||||
FS_TYPE filesysType;
|
||||
uint64_t totalSize;
|
||||
sec_t rootDirStart;
|
||||
uint32_t rootDirCluster;
|
||||
uint32_t numberOfSectors;
|
||||
sec_t dataStart;
|
||||
uint32_t bytesPerSector;
|
||||
uint32_t sectorsPerCluster;
|
||||
uint32_t bytesPerCluster;
|
||||
uint32_t fsInfoSector;
|
||||
FAT fat;
|
||||
// Values that may change after construction
|
||||
uint32_t cwdCluster; // Current working directory cluster
|
||||
int openFileCount;
|
||||
struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files
|
||||
mutex_t lock; // A lock for partition operations
|
||||
bool readOnly; // If this is set, then do not try writing to the disc
|
||||
char label[12]; // Volume label
|
||||
} PARTITION;
|
||||
|
||||
/*
|
||||
Mount the supplied device and return a pointer to the struct necessary to use it
|
||||
*/
|
||||
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector);
|
||||
|
||||
/*
|
||||
Dismount the device and free all structures used.
|
||||
Will also attempt to synchronise all open files to disc.
|
||||
*/
|
||||
void _FAT_partition_destructor (PARTITION* partition);
|
||||
|
||||
/*
|
||||
Return the partition specified in a path, as taken from the devoptab.
|
||||
*/
|
||||
PARTITION* _FAT_partition_getPartitionFromPath (const char* path);
|
||||
|
||||
/*
|
||||
Create the fs info sector.
|
||||
*/
|
||||
void _FAT_partition_createFSinfo(PARTITION * partition);
|
||||
|
||||
/*
|
||||
Read the fs info sector data.
|
||||
*/
|
||||
void _FAT_partition_readFSinfo(PARTITION * partition);
|
||||
|
||||
/*
|
||||
Write the fs info sector data.
|
||||
*/
|
||||
void _FAT_partition_writeFSinfo(PARTITION * partition);
|
||||
|
||||
#endif // _PARTITION_H
|
||||
253
arm9/src/main.c
253
arm9/src/main.c
@ -17,52 +17,22 @@
|
||||
/*
|
||||
|
||||
TODO:
|
||||
- LESS NAND WRITES!!!!!
|
||||
- Write good NAND read routine
|
||||
- Detect debugger vs dev
|
||||
- Create FAT
|
||||
|
||||
Okay so this might be a bad idea in my hands lol
|
||||
Find an FS lib? No. Fuck no.
|
||||
|
||||
Paste this (encrypted) into sector 0x877 (0x10EE00).
|
||||
|
||||
unsigned char twlMain[56] = {
|
||||
0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
|
||||
0x02, 0x20, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x34, 0x00,
|
||||
0x20, 0x00, 0x10, 0x00, 0x77, 0x08, 0x00, 0x00, 0x89, 0x6F, 0x06, 0x00,
|
||||
0x00, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00
|
||||
};
|
||||
|
||||
Pad to 0x200b and make sure to have 0x55AA (CRINGE I HATE MBR I HATE MBR I HATE MBR) <-- I don't even remember writing this.
|
||||
Now fill 0x200*B88b (total 0x171000) afterwards with zerobytes.
|
||||
|
||||
Congrats. This is a formatted file system. Please don't hurt me.
|
||||
|
||||
Okay but what about twl_photo? Uhhh nobody likes that.... oh fine.
|
||||
|
||||
Paste this (encrypted) into sector 0x6784D (0xCF09A00)
|
||||
|
||||
unsigned char twlPhoto[56] = {
|
||||
0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
|
||||
0x02, 0x20, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x09, 0x00,
|
||||
0x20, 0x00, 0x10, 0x00, 0x4D, 0x78, 0x06, 0x00, 0xB3, 0x05, 0x01, 0x00,
|
||||
0x01, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00
|
||||
};
|
||||
|
||||
Same thing but fill 0x200*13Ab (total 0x27400) afterwards with zerobytes.
|
||||
|
||||
Okay lol how do I do the VBR padding to 0x200
|
||||
- Dump VBR start into a variable
|
||||
- for loop to add the rest of the zero bytes (0x1C8b)
|
||||
- end with 0x55AA
|
||||
|
||||
Awesome!
|
||||
|
||||
- Fancy windows like TWL EVA
|
||||
- Recover HWInfo
|
||||
- Back up/restore screen memory
|
||||
- System transfer (way later on)
|
||||
|
||||
- Why doesn't unmounting NAND get reflected in the file test?
|
||||
|
||||
*/
|
||||
extern bool nand_Startup();
|
||||
extern bool sdio_Startup();
|
||||
bool nandMounted = false;
|
||||
bool sdMounted = false;
|
||||
bool agingMode = false;
|
||||
bool success = false;
|
||||
|
||||
bool programEnd = false;
|
||||
bool sdnandMode = true;
|
||||
@ -80,12 +50,13 @@ PrintConsole topScreen;
|
||||
PrintConsole bottomScreen;
|
||||
|
||||
typedef enum {
|
||||
MENUSTATE_FS_MENU,
|
||||
MENUSTATE_NF_MENU,
|
||||
MENUSTATE_NULL,
|
||||
MENUSTATE_TEST,
|
||||
MENUSTATE_TEST2,
|
||||
MENUSTATE_EXIT
|
||||
STARTMENU_FS_MENU,
|
||||
STARTMENU_NF_MENU,
|
||||
STARTMENU_NULL,
|
||||
STARTMENU_TEST,
|
||||
STARTMENU_TEST2,
|
||||
STARTMENU_TEST3,
|
||||
STARTMENU_EXIT
|
||||
} MenuState;
|
||||
|
||||
static int _mainMenu(int cursor)
|
||||
@ -102,8 +73,9 @@ static int _mainMenu(int cursor)
|
||||
addMenuItem(m, "FileSystem Menu", NULL, 0, "Options such as repairing MBR\n and formatting twl_main/photo.");
|
||||
addMenuItem(m, "NandFirm menu", NULL, 0, "NandFirm (stage2) installers\n and version testing.");
|
||||
addMenuItem(m, "---------------", NULL, 0, "");
|
||||
addMenuItem(m, "Debug1", NULL, 0, "Testing area");
|
||||
addMenuItem(m, "Debug2", NULL, 0, "Testing area");
|
||||
addMenuItem(m, "Debug1", NULL, 0, "Font display.");
|
||||
addMenuItem(m, "Debug2", NULL, 0, "MBR corruption test.");
|
||||
addMenuItem(m, "Debug3", NULL, 0, "NAND AGING test.");
|
||||
addMenuItem(m, "Exit", NULL, 0, "Leave the program.");
|
||||
|
||||
m->cursor = (cursor);
|
||||
@ -179,41 +151,29 @@ int main(int argc, char **argv)
|
||||
fifoSetValue32Handler(FIFO_USER_01, fifoHandlerPower, NULL);
|
||||
fifoSetValue32Handler(FIFO_USER_03, fifoHandlerBattery, NULL);
|
||||
|
||||
//DSi check
|
||||
iprintf("\x1B[30m");
|
||||
|
||||
if (!isDSiMode()) {
|
||||
messageBox("\x1B[31mError:\x1B[33m This app is only for DSi.");
|
||||
messageBox("\x1B[31mError:\x1B[30m This app is only for DSi.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//setup sd card access
|
||||
if (!fatInitDefault()) {
|
||||
messageBox("fatInitDefault()...\x1B[31mFailed\n\x1B[30m\n\nSome features will not work.");
|
||||
//return 0;
|
||||
agingMode = true;
|
||||
|
||||
if (!sdio_Startup())
|
||||
{
|
||||
messageBox("\n\x1B[31mERROR: \x1B[30mFailed to mount SD!\n\nSome features will not work.");
|
||||
}
|
||||
|
||||
//setup sd card access
|
||||
if(!nitroFSInit(argv[0])) {
|
||||
if(!nitroFSInit("TwlNandTool.prod.srl") || !nitroFSGood()) {
|
||||
if(!nitroFSInit("TwlNandTool.dev.srl") || !nitroFSGood()) {
|
||||
if(!nitroFSInit("ntrboot.nds") || !nitroFSGood()) {
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
messageBox("nitroFSInit()...\x1B[31mFailed\n\x1B[30m\nSome features will not work.\n\nTry placing the SRL for your DSion your SD card root like this:\n\nSDMC:/TwlNandTool.prod.srl\nSDMC:/TwlNandTool.dev.srl\nSDMC:/ntrboot.nds\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!nand_Startup())
|
||||
{
|
||||
messageBox("\n\x1B[31mFATAL:\x1B[30mStart NAND failed!\nPlease make an issue on GitHub.\n\nThe program will end soon.");
|
||||
}
|
||||
|
||||
//setup nand access
|
||||
if (!fatMountSimple("nand", &io_dsi_nand)) {
|
||||
messageBox("nand init \x1B[31mfailed\n\x1B[30m\n\nNAND must be repaired.");
|
||||
}
|
||||
agingMode = true;
|
||||
mountMain();
|
||||
mountNitroFS();
|
||||
agingMode = false;
|
||||
|
||||
clearScreen(cSUB);
|
||||
clearScreen(cMAIN);
|
||||
@ -227,26 +187,30 @@ int main(int argc, char **argv)
|
||||
switch (cursor)
|
||||
{
|
||||
|
||||
case MENUSTATE_FS_MENU:
|
||||
case STARTMENU_FS_MENU:
|
||||
fsMain();
|
||||
break;
|
||||
|
||||
case MENUSTATE_NF_MENU:
|
||||
case STARTMENU_NF_MENU:
|
||||
nfMain();
|
||||
break;
|
||||
|
||||
case MENUSTATE_NULL:
|
||||
case STARTMENU_NULL:
|
||||
break;
|
||||
|
||||
case MENUSTATE_TEST:
|
||||
case STARTMENU_TEST:
|
||||
debug1();
|
||||
break;
|
||||
|
||||
case MENUSTATE_TEST2:
|
||||
case STARTMENU_TEST2:
|
||||
debug2();
|
||||
break;
|
||||
|
||||
case MENUSTATE_EXIT:
|
||||
case STARTMENU_TEST3:
|
||||
debug3();
|
||||
break;
|
||||
|
||||
case STARTMENU_EXIT:
|
||||
programEnd = true;
|
||||
break;
|
||||
}
|
||||
@ -254,9 +218,14 @@ int main(int argc, char **argv)
|
||||
|
||||
clearScreen(cSUB);
|
||||
printf("Unmounting NAND...\n");
|
||||
fatUnmount("nand:");
|
||||
printf("Merging stages...\n");
|
||||
nandio_shutdown();
|
||||
if(nandMounted) {
|
||||
fatUnmount("nand");
|
||||
}
|
||||
// I think this was some safety thing but it wants to re-write the entire NAND...
|
||||
// I'm the one person always saying "NANDs aren't that weak", so you know it's excessive when I comment it out.
|
||||
|
||||
//printf("Merging stages...\n");
|
||||
//nandio_shutdown();
|
||||
|
||||
fifoSendValue32(FIFO_USER_02, 0x54495845); // 'EXIT'
|
||||
|
||||
@ -278,74 +247,76 @@ int debug1(void) {
|
||||
printf("%c ", (char)i);
|
||||
}
|
||||
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
}
|
||||
|
||||
int debug2(void) {
|
||||
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Debug1");
|
||||
iprintf("\n Stupid AES-CTR BS ");
|
||||
iprintf("\n>> Corrupt MBR ");
|
||||
iprintf("\n--------------------------------");
|
||||
memset(sector_buf, 0, 0x200);
|
||||
iprintf("\nWriting new MBR...");
|
||||
nand_WriteSectors(0, 1, sector_buf);
|
||||
iprintf("\nTesting new MBR...");
|
||||
nand_ReadSectors(0, 1, sector_buf);
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
if(!parse_mbr(sector_buf, is3DS)) {
|
||||
iprintf("\n\n \x1B[31mERROR!\x1B[30m Failed to break MBR.");
|
||||
} else {
|
||||
iprintf("\n\x1B[32mMBR corrupted okay!\x1B[30m");
|
||||
}
|
||||
|
||||
exitFunction();
|
||||
}
|
||||
|
||||
int debug3(void) {
|
||||
success = true;
|
||||
agingMode = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> NAND AGING tester ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
nand_ReadSectors(877, 1, sector_buf);
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 877, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
|
||||
printf("\n ");
|
||||
for (int i = 452; i < SECTOR_SIZE; i++) {
|
||||
printf("%02X", sector_buf[i]);
|
||||
if ((i + 1) % 2 == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
if ((i - 443) % 8 == 0 && i != 444) {
|
||||
printf("\n ");
|
||||
}
|
||||
nandPrintInfo();
|
||||
if (!nandFirmImport(true)) {
|
||||
success = false;
|
||||
}
|
||||
nandFirmRead();
|
||||
if (!repairMbr(true)) {
|
||||
success = false;
|
||||
}
|
||||
readMbr();
|
||||
|
||||
iprintf("\n\n Read in");
|
||||
if (success == true && !mountMain()) {
|
||||
if (!formatMain() && !formatPhoto() && !mountMain()) {
|
||||
iprintf("\nNAND mount failed!");
|
||||
success = false;
|
||||
} else {
|
||||
nandMounted = true;
|
||||
}
|
||||
} else if (nandMounted == true) {
|
||||
iprintf("\nNAND already mounted.");
|
||||
} else {
|
||||
nandMounted = true;
|
||||
}
|
||||
filetestMain();
|
||||
unmountMain();
|
||||
filetestNitro();
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
agingMode = false;
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
iprintf("\n>> NAND AGING tester result ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (success == true) {
|
||||
iprintf("\nAll tests passed okay.");
|
||||
} else {
|
||||
iprintf("\nTests failed!");
|
||||
}
|
||||
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 877, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
|
||||
printf("\n ");
|
||||
for (int i = 444; i < SECTOR_SIZE; i++) {
|
||||
printf("%02X", sector_buf[i]);
|
||||
if ((i + 1) % 2 == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
if ((i - 443) % 8 == 0 && i != 444) {
|
||||
printf("\n ");
|
||||
}
|
||||
}
|
||||
|
||||
iprintf("\n\n Read out");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void clearScreen(enum console c)
|
||||
|
||||
@ -10,6 +10,11 @@
|
||||
#include "video.h"
|
||||
#include "nitrofs.h"
|
||||
|
||||
extern bool nandMounted;
|
||||
extern bool sdMounted;
|
||||
extern bool agingMode;
|
||||
extern bool success;
|
||||
|
||||
extern bool programEnd;
|
||||
extern bool sdnandMode;
|
||||
extern bool unlaunchFound;
|
||||
@ -27,6 +32,7 @@ void backupMenu();
|
||||
void testMenu();
|
||||
int debug1();
|
||||
int debug2();
|
||||
int debug3();
|
||||
|
||||
extern PrintConsole topScreen;
|
||||
extern PrintConsole bottomScreen;
|
||||
|
||||
@ -339,10 +339,33 @@ bool moveCursor(Menu* m)
|
||||
return !(lastCursor == m->cursor);
|
||||
}
|
||||
|
||||
|
||||
void wait(int ticks){
|
||||
while(ticks--)swiWaitForVBlank();
|
||||
}
|
||||
|
||||
char downloadPlayLoading(int number) {
|
||||
char pictoload[] = {(char)142, (char)143, (char)144, (char)145, (char)146, (char)147, (char)148, (char)149};
|
||||
static int counter = 0;
|
||||
|
||||
counter = (counter % 7) + 1;
|
||||
return pictoload[counter];
|
||||
}
|
||||
|
||||
void exitFunction() {
|
||||
if (agingMode == false) {
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
wait(50);
|
||||
}
|
||||
clearScreen(cSUB);
|
||||
// Restore screen memory here
|
||||
}
|
||||
@ -40,5 +40,5 @@ void printMenu(Menu* m, int level);
|
||||
bool moveCursor(Menu* m);
|
||||
|
||||
char downloadPlayLoading(int number);
|
||||
|
||||
void exitFunction();
|
||||
#endif
|
||||
@ -4,6 +4,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <nds/arm9/nand.h>
|
||||
#include <nds/disc_io.h>
|
||||
#include "f_xy.h"
|
||||
#include "twltool/dsi.h"
|
||||
#include "nandio.h"
|
||||
@ -15,6 +16,9 @@
|
||||
#include "../main.h"
|
||||
#include "../video.h"
|
||||
#include "../menu.h"
|
||||
#include "../lib/libfat/include/fat.h"
|
||||
|
||||
extern bool nand_Startup();
|
||||
|
||||
// Master Boot Records
|
||||
u8 mbrSamsung[68] = {
|
||||
@ -50,54 +54,21 @@ u8 vbrPhoto[54] = {
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20
|
||||
};
|
||||
|
||||
// File tables for /sys/ folder and HWInfo secure
|
||||
// Why? HWInfo is always at one of 3 set offsets. I use those offsets for HWInfo recovery.
|
||||
// We will copy over these file tables to ensure the offsets stay the same.
|
||||
|
||||
// No idea what this is but I need it lol
|
||||
u8 fileTable1[16] = {
|
||||
0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0x00, 0x00
|
||||
};
|
||||
|
||||
// File table for /sys/ folder
|
||||
u8 fileTable2[64] = {
|
||||
0x41, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F,
|
||||
0x00, 0x54, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x53, 0x59, 0x53, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x21, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x28, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// File table for HWInfo
|
||||
u8 fileTable3[192] = {
|
||||
0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0x28, 0x00, 0x00, 0x00, 0x00, 0x75, 0x02,
|
||||
0x64, 0x59, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x2E, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x21, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x28, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xE5, 0x6C, 0x00, 0x6F, 0x00, 0x67, 0x00, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xE5, 0x4F, 0x47, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x00, 0x00, 0x01, 0x00, 0x21, 0x28, 0x64, 0x59, 0x00, 0x00, 0x01, 0x00,
|
||||
0x21, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x48, 0x00, 0x57,
|
||||
0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x0F, 0x00, 0x9C, 0x4F, 0x00,
|
||||
0x5F, 0x00, 0x53, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x00, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x48, 0x57, 0x49, 0x4E, 0x46, 0x4F, 0x5F, 0x53,
|
||||
0x44, 0x41, 0x54, 0x20, 0x00, 0x00, 0x01, 0x00, 0x21, 0x28, 0x64, 0x59,
|
||||
0x00, 0x00, 0x50, 0x02, 0x64, 0x59, 0x06, 0x00, 0x00, 0x40, 0x00, 0x00
|
||||
};
|
||||
|
||||
static size_t i;
|
||||
|
||||
enum {
|
||||
READ_MBR,
|
||||
REPAIR_MBR,
|
||||
FORMAT_MAIN,
|
||||
FORMAT_PHOTO,
|
||||
BACK
|
||||
FSMENU_READ_MBR,
|
||||
FSMENU_REPAIR_MBR,
|
||||
FSMENU_FORMAT_MAIN,
|
||||
FSMENU_FORMAT_PHOTO,
|
||||
FSMENU_NULL,
|
||||
FSMENU_MOUNT_MAIN,
|
||||
FSMENU_UNMOUNT_MAIN,
|
||||
FSMENU_MOUNT_NITRO,
|
||||
FSMENU_NULL2,
|
||||
FSMENU_FILETEST_MAIN,
|
||||
FSMENU_FILETEST_NITRO,
|
||||
FSMENU_BACK
|
||||
};
|
||||
|
||||
static int _fsMenu(int cursor)
|
||||
@ -112,6 +83,13 @@ static int _fsMenu(int cursor)
|
||||
addMenuItem(m, "Repair MBR", NULL, 0, "Repair the Master Boot Record.");
|
||||
addMenuItem(m, "Format TWL_MAIN", NULL, 0, "Format the partition where the\n firmware, apps, and saves are\n installed.\n\n THIS WILL ERASE EVERYTHING.");
|
||||
addMenuItem(m, "Format TWL_PHOTO", NULL, 0, "Format the partition where\n photos are stored.\n\n THIS WILL ERASE EVERYTHING.");
|
||||
addMenuItem(m, "------------------", NULL, 0, "");
|
||||
addMenuItem(m, "Mount TWL_MAIN", NULL, 0, "Mount the TWL_MAIN partition.");
|
||||
addMenuItem(m, "Unmount TWL_MAIN", NULL, 0, "Unmount the TWL_MAIN partition");
|
||||
addMenuItem(m, "Mount NitroFS", NULL, 0, "Mount the ROM filesystem.");
|
||||
addMenuItem(m, "------------------", NULL, 0, "");
|
||||
addMenuItem(m, "File test TWL_MAIN", NULL, 0, "Attempt to make/delete dummy\n file on NAND.");
|
||||
addMenuItem(m, "File test NitroFS", NULL, 0, "Attempt to read file in\n NitroFS.");
|
||||
addMenuItem(m, "Back", NULL, 0, "Leave the FileSystem menu.");
|
||||
|
||||
m->cursor = cursor;
|
||||
@ -149,23 +127,49 @@ int fsMain(void)
|
||||
switch (cursor)
|
||||
{
|
||||
|
||||
case READ_MBR:
|
||||
case FSMENU_READ_MBR:
|
||||
readMbr();
|
||||
break;
|
||||
|
||||
case REPAIR_MBR:
|
||||
repairMbr();
|
||||
case FSMENU_REPAIR_MBR:
|
||||
repairMbr(false);
|
||||
break;
|
||||
|
||||
case FORMAT_MAIN:
|
||||
case FSMENU_FORMAT_MAIN:
|
||||
formatMain();
|
||||
break;
|
||||
|
||||
case FORMAT_PHOTO:
|
||||
case FSMENU_FORMAT_PHOTO:
|
||||
formatPhoto();
|
||||
break;
|
||||
|
||||
case BACK:
|
||||
case FSMENU_NULL:
|
||||
break;
|
||||
|
||||
case FSMENU_MOUNT_MAIN:
|
||||
mountMain();
|
||||
break;
|
||||
|
||||
case FSMENU_UNMOUNT_MAIN:
|
||||
unmountMain();
|
||||
break;
|
||||
|
||||
case FSMENU_MOUNT_NITRO:
|
||||
mountNitroFS();
|
||||
break;
|
||||
|
||||
case FSMENU_NULL2:
|
||||
break;
|
||||
|
||||
case FSMENU_FILETEST_MAIN:
|
||||
filetestMain();
|
||||
break;
|
||||
|
||||
case FSMENU_FILETEST_NITRO:
|
||||
filetestNitro();
|
||||
break;
|
||||
|
||||
case FSMENU_BACK:
|
||||
programEnd = true;
|
||||
break;
|
||||
}
|
||||
@ -176,15 +180,20 @@ int fsMain(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int readMbr(void) {
|
||||
bool readMbr(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
nand_ReadSectors(0, 1, sector_buf);
|
||||
dsi_crypt_init((const u8*)consoleIDfixed, (const u8*)0x2FFD7BC, is3DS);
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
|
||||
iprintf("\n>> MBR (Master Boot Record) ");
|
||||
iprintf("\n--------------------------------");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if(nand_ReadSectors(0, 1, sector_buf) == false) {
|
||||
iprintf("\nCouldn't read NAND!\n");
|
||||
success = false;
|
||||
}
|
||||
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
|
||||
printf("\n ");
|
||||
for (i = 444; i < SECTOR_SIZE; i++) {
|
||||
printf("%02X", sector_buf[i]);
|
||||
@ -197,124 +206,103 @@ int readMbr(void) {
|
||||
}
|
||||
if(parse_mbr(sector_buf, is3DS)) {
|
||||
iprintf("\n\n \x1B[31mERROR!\x1B[30m MBR is corrupted.");
|
||||
success = false;
|
||||
}
|
||||
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
int repairMbr(void) {
|
||||
bool repairMbr(bool autofix) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Repair Corrupted MBR ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if(!parse_mbr(sector_buf, is3DS)) {
|
||||
if (choicePrint("NAND does not look corrupt.\nRepair anyway?") == NO) {
|
||||
return 0;
|
||||
if (parse_mbr(sector_buf, is3DS) || nandMounted == false) {
|
||||
if(!parse_mbr(sector_buf, is3DS) && autofix == false) {
|
||||
if (choicePrint("NAND does not look corrupt.\nRepair anyway?") == NO) {
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iprintf("\nNAND type : %s (0x%02X)", nandInfo.NAND_PNM, nandInfo.NAND_MID);
|
||||
iprintf("\nNAND type : %s (0x%02X)", nandInfo.NAND_PNM, nandInfo.NAND_MID);
|
||||
|
||||
memset(sector_buf, 0, 444);
|
||||
if (nandInfo.NAND_MID == 0x15) {
|
||||
memcpy(sector_buf + 444, mbrSamsung, sizeof(mbrSamsung));
|
||||
} else if (nandInfo.NAND_MID == 0xFE) {
|
||||
memcpy(sector_buf + 444, mbrSt, sizeof(mbrSt));
|
||||
}
|
||||
memset(sector_buf, 0, 444);
|
||||
if (nandInfo.NAND_MID == 0x15) {
|
||||
memcpy(sector_buf + 444, mbrSamsung, sizeof(mbrSamsung));
|
||||
} else if (nandInfo.NAND_MID == 0xFE) {
|
||||
memcpy(sector_buf + 444, mbrSt, sizeof(mbrSt));
|
||||
}
|
||||
|
||||
// Write new MBR, encrypt it, then save it.
|
||||
// Afterwards we read it back from NAND and decrypt to confirm it works.
|
||||
//
|
||||
// No need to do safety checks before writing since corrupted MBR is already broken.
|
||||
iprintf("\nWriting new MBR...");
|
||||
// Write new MBR, encrypt it, then save it.
|
||||
// Afterwards we read it back from NAND and decrypt to confirm it works.
|
||||
//
|
||||
// No need to do safety checks before writing since corrupted MBR is already broken.
|
||||
iprintf("\nWriting new MBR...");
|
||||
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
nand_WriteSectors(0, 1, sector_buf);
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
nand_WriteSectors(0, 1, sector_buf);
|
||||
|
||||
iprintf("\nTesting new MBR...");
|
||||
iprintf("\nTesting new MBR...");
|
||||
|
||||
nand_ReadSectors(0, 1, sector_buf);
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
nand_ReadSectors(0, 1, sector_buf);
|
||||
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
|
||||
|
||||
if(parse_mbr(sector_buf, is3DS)) {
|
||||
iprintf("\n\n \x1B[31mERROR!\x1B[30m Failed to fix MBR.");
|
||||
if(parse_mbr(sector_buf, is3DS)) {
|
||||
iprintf("\n\n \x1B[31mERROR!\x1B[30m Failed to fix MBR.");
|
||||
success = false;
|
||||
} else {
|
||||
iprintf("\n\x1B[32mThe new MBR passed!\x1B[30m");
|
||||
}
|
||||
} else {
|
||||
iprintf("\n\x1B[32mThe new MBR passed!\x1B[30m");
|
||||
iprintf("\nNAND is currently mounted!\n\nSkipping MBR repair since\nthe FS had no issues.");
|
||||
}
|
||||
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
int formatMain(void) {
|
||||
bool formatMain(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Format TWL_MAIN ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
iprintf("\n\nWriting VBR...");
|
||||
memset(file_buf, 0, 0x200);
|
||||
// Write the first 54 bytes, then pad to 0x1FE. Finally write 0x55AA
|
||||
memcpy(file_buf, vbrMain, sizeof(vbrMain));
|
||||
memset(file_buf + sizeof(vbrMain), 0, (BUFFER_SIZE - sizeof(vbrMain) - 2));
|
||||
memset(file_buf + SECTOR_SIZE - 2, 0x55, 1);
|
||||
memset(file_buf + SECTOR_SIZE - 1, 0xAA, 1);
|
||||
good_nandio_write(0x10EE00, 0x200, file_buf, true);
|
||||
iprintf("\nClearing file tables...");
|
||||
memset(file_buf, 0, 0x600);
|
||||
for (i = 0; i < 0x3d8; i++) {
|
||||
good_nandio_write(0x10EE00 + 0x200 + (0x600 * i), 0x600, file_buf, true);
|
||||
if (nandMounted == false) {
|
||||
iprintf("\nWriting VBR...");
|
||||
memset(file_buf, 0, 0x200);
|
||||
// Write the first 54 bytes, then pad to 0x1FE. Finally write 0x55AA
|
||||
memcpy(file_buf, vbrMain, sizeof(vbrMain));
|
||||
memset(file_buf + sizeof(vbrMain), 0, (BUFFER_SIZE - sizeof(vbrMain) - 2));
|
||||
memset(file_buf + SECTOR_SIZE - 2, 0x55, 1);
|
||||
memset(file_buf + SECTOR_SIZE - 1, 0xAA, 1);
|
||||
good_nandio_write(0x10EE00, 0x200, file_buf, true);
|
||||
iprintf("\nClearing file tables...");
|
||||
memset(file_buf, 0, 0x600);
|
||||
for (i = 0; i < 0x3d8; i++) {
|
||||
good_nandio_write(0x10EE00 + 0x200 + (0x600 * i), 0x600, file_buf, true);
|
||||
}
|
||||
iprintf("\nAll done!");
|
||||
} else {
|
||||
iprintf("\nNAND is currently mounted!\n\nSkipping TWL_MAIN format since\nthe FS had no issues.");
|
||||
}
|
||||
iprintf("\nMaking new file tables...");
|
||||
memset(file_buf, 0, 0x200);
|
||||
memcpy(file_buf, fileTable1, sizeof(fileTable1));
|
||||
good_nandio_write(0x115800, sizeof(fileTable1), file_buf, true);
|
||||
memset(file_buf, 0, 0x200);
|
||||
memcpy(file_buf, fileTable2, sizeof(fileTable2));
|
||||
good_nandio_write(0x11C000, sizeof(fileTable2), file_buf, true);
|
||||
memset(file_buf, 0, 0x200);
|
||||
memcpy(file_buf, fileTable3, sizeof(fileTable3));
|
||||
good_nandio_write(0x120000, sizeof(fileTable3), file_buf, true);
|
||||
|
||||
iprintf("\nAll done!");
|
||||
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
int formatPhoto(void) {
|
||||
bool formatPhoto(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Format TWL_PHOTO ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
iprintf("\n\nWriting VBR...");
|
||||
iprintf("\nWriting VBR...");
|
||||
memset(file_buf, 0, 0x200);
|
||||
// Write the first 54 bytes, then pad to 0x1FE. Finally write 0x55AA
|
||||
memcpy(file_buf, vbrPhoto, sizeof(vbrPhoto));
|
||||
@ -330,14 +318,208 @@ int formatPhoto(void) {
|
||||
|
||||
iprintf("\nAll done!");
|
||||
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
bool mountMain(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
iprintf("\n>> Mount TWL_MAIN ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (nandMounted == true) {
|
||||
if (!agingMode) {
|
||||
agingMode = true;
|
||||
unmountMain();
|
||||
agingMode = false;
|
||||
} else {
|
||||
unmountMain();
|
||||
}
|
||||
}
|
||||
|
||||
clearScreen(cSUB);
|
||||
iprintf("\n>> Mount TWL_MAIN ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (!nandio_startup()) {
|
||||
iprintf("\nFailed startup!");
|
||||
success = false;
|
||||
}
|
||||
if (success == true && !fatMountSimple("nand", &io_dsi_nand, true)) {
|
||||
iprintf("\n\x1B[31mMount TWL_MAIN failed!\n\x1B[30m\nNAND must be repaired. Try...\n - Fixing MBR\n - Formatting TWL_MAIN");
|
||||
success = false;
|
||||
} else if (success == true) {
|
||||
iprintf("\nTWL_MAIN mounted okay.");
|
||||
nandio_unlock_writing();
|
||||
nandMounted = true;
|
||||
}
|
||||
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool unmountMain(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Unmount TWL_MAIN ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (nandMounted == true) {
|
||||
iprintf("\nUnmounting NAND...");
|
||||
fatUnmount("nand");
|
||||
nandMounted = false;
|
||||
sdMounted = false;
|
||||
|
||||
// I just don't want to interrupt the startup aging test
|
||||
if (!agingMode) {
|
||||
agingMode = true;
|
||||
success = mountNitroFS();
|
||||
agingMode = false;
|
||||
} else {
|
||||
success = mountNitroFS();
|
||||
}
|
||||
|
||||
clearScreen(cSUB);
|
||||
iprintf("\n>> Umount TWL_MAIN ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (success) {
|
||||
iprintf("\nTWL_MAIN unmounted okay.");
|
||||
} else {
|
||||
iprintf("\nFailed to mount TWL_MAIN!");
|
||||
}
|
||||
|
||||
} else {
|
||||
iprintf("\nNAND not mounted!\nDoing nothing.");
|
||||
}
|
||||
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool mountNitroFS(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Mount NitroFS ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (sdMounted == false && !fatInitDefault()) {
|
||||
iprintf("\nSD card not mounted!");
|
||||
success = false;
|
||||
} else {
|
||||
iprintf("\nSD card mounted okay.");
|
||||
sdMounted = true;
|
||||
}
|
||||
|
||||
if(!nitroFSInit("TwlNandTool.prod.srl") || !nitroFSGood()) {
|
||||
if(!nitroFSInit("TwlNandTool.dev.srl") || !nitroFSGood()) {
|
||||
if(!nitroFSInit("ntrboot.nds") || !nitroFSGood()) {
|
||||
iprintf("\x1B[31mFailed to mount NitroFS!\n\x1B[30m\nSome features will not work.\n\nTry placing the SRL for your DSiat one of these locations:\n\nSDMC:/TwlNandTool.prod.srl\nSDMC:/TwlNandTool.dev.srl\nSDMC:/ntrboot.nds\n");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
iprintf("\nNitroFS mounted okay.");
|
||||
}
|
||||
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool filetestMain(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Read TWL_MAIN file ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
if (nandMounted == true) {
|
||||
printf("\nMaking temp folder...");
|
||||
mkdir("nand:/tmp", 0777);
|
||||
|
||||
printf("\nOpening test file...\n");
|
||||
|
||||
char file_path[100];
|
||||
snprintf(file_path, 100, "nand:/tmp/test.bin");
|
||||
FILE *file = fopen(file_path, "wb");
|
||||
printf("\n%s\n", file_path);
|
||||
if(file) {
|
||||
iprintf("\nWrite test...");
|
||||
memset(sector_buf, 0, SECTOR_SIZE);
|
||||
memcpy(sector_buf + 444, mbrSamsung, sizeof(mbrSamsung));
|
||||
fwrite(sector_buf, 1, SECTOR_SIZE, file);
|
||||
fclose(file);
|
||||
iprintf("\nFile closed.");
|
||||
} else {
|
||||
success = false;
|
||||
iprintf("\nFile failed to open!");
|
||||
}
|
||||
|
||||
memset(sector_buf, 0, SECTOR_SIZE);
|
||||
|
||||
iprintf("\nOpening file...");
|
||||
FILE *file2 = fopen(file_path, "rb");
|
||||
if(file2) {
|
||||
iprintf("\nRead test...");
|
||||
fread(sector_buf, 1, SECTOR_SIZE, file2);
|
||||
if(parse_mbr(sector_buf, is3DS)) {
|
||||
iprintf("\n\n \x1B[31mERROR!\x1B[30m File did not save!");
|
||||
success = false;
|
||||
} else {
|
||||
iprintf("\n\x1B[32mThe file is okay!\x1B[30m");
|
||||
}
|
||||
fclose(file2);
|
||||
iprintf("\nFile closed.");
|
||||
} else {
|
||||
success = false;
|
||||
iprintf("\nFile failed to open!");
|
||||
}
|
||||
} else {
|
||||
success = false;
|
||||
iprintf("\nTWL_MAIN is not mounted!");
|
||||
}
|
||||
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool filetestNitro(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Read NitroFS file ");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
printf("\nOpening test file...\n");
|
||||
|
||||
char file_path[100];
|
||||
snprintf(file_path, 100, "nitro:/version_twlnandtool");
|
||||
FILE *file = fopen(file_path, "r");
|
||||
printf("\n%s\n", file_path);
|
||||
if(file) {
|
||||
fseek(file, 0, SEEK_END);
|
||||
int length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
char *version = (char *)malloc((length) * sizeof(char));
|
||||
version[length] = '\0';
|
||||
|
||||
fread(version, 1, length, file);
|
||||
|
||||
printf("\n%s\n", version);
|
||||
fclose(file);
|
||||
|
||||
iprintf("\nFile opened okay.");
|
||||
} else {
|
||||
success = false;
|
||||
iprintf("\nFile failed to open!");
|
||||
}
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
@ -16,7 +16,13 @@
|
||||
#include "../menu.h"
|
||||
|
||||
int fsMain(void);
|
||||
int readMbr(void);
|
||||
int repairMbr(void);
|
||||
int formatMain(void);
|
||||
int formatPhoto(void);
|
||||
bool readMbr(void);
|
||||
bool repairMbr(bool autofix);
|
||||
bool formatMain(void);
|
||||
bool formatPhoto(void);
|
||||
bool mountMain(void);
|
||||
bool unmountMain(void);
|
||||
bool mountNitroFS(void);
|
||||
|
||||
bool filetestMain(void);
|
||||
bool filetestNitro(void);
|
||||
@ -14,8 +14,6 @@
|
||||
#include "../main.h"
|
||||
#include "../video.h"
|
||||
|
||||
bool success = true;
|
||||
|
||||
void death(char *message, u8 *buffer){
|
||||
iprintf("\n%s\n", message);
|
||||
free(buffer);
|
||||
@ -108,8 +106,8 @@ int nfMain(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nandFirmRead(void) {
|
||||
|
||||
bool nandFirmRead(void) {
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> NandFirm Version Checker ");
|
||||
@ -117,11 +115,7 @@ int nandFirmRead(void) {
|
||||
|
||||
int fail=0;
|
||||
|
||||
// Sectors are 0x200 each, this is sector 626 OR offset 0x4E400
|
||||
// nand_ReadSectors(<start at sector>, <number of sectors to read>, <save sectors to>)
|
||||
if(nand_ReadSectors(626, 1, sector_buf) == false){
|
||||
iprintf("Nand read error");
|
||||
}
|
||||
nand_ReadSectors(626, 1, sector_buf);
|
||||
|
||||
printf("\n Ver: ");
|
||||
for (i = 0; i < 10; i++) {
|
||||
@ -136,28 +130,18 @@ int nandFirmRead(void) {
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
iprintf("\n\n\n Please Push Select To Return ");
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool nandFirmImport(bool sdmc) {
|
||||
|
||||
success = true;
|
||||
clearScreen(cSUB);
|
||||
|
||||
iprintf("\n>> Import NandFirm (%s) ", sdmc ? "sdmc" : "menu");
|
||||
iprintf("\n--------------------------------");
|
||||
|
||||
printf("Opening NandFirm...\n");
|
||||
printf("\nOpening NandFirm...\n");
|
||||
|
||||
char file_path[100];
|
||||
snprintf(file_path, 100, "nitro:/import/%s/%s-launcher.nand", consoleSignName, sdmc ? "sdmc" : "menu");
|
||||
@ -179,21 +163,12 @@ bool nandFirmImport(bool sdmc) {
|
||||
iprintf("\nNandFirm import failed!");
|
||||
}
|
||||
|
||||
iprintf("\n\n Please Push Select To Return ");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
return success;
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
|
||||
int nandPrintInfo(void) {
|
||||
|
||||
bool nandPrintInfo(void) {
|
||||
success = true;
|
||||
extern nandData nandInfo;
|
||||
clearScreen(cSUB);
|
||||
iprintf("\n>> CID (Card IDentification)");
|
||||
@ -216,15 +191,7 @@ int nandPrintInfo(void) {
|
||||
printf("\n ");
|
||||
}
|
||||
}
|
||||
iprintf("\n\n\n Please Push Select To Return ");
|
||||
|
||||
while (true)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (keysDown() & KEY_SELECT )
|
||||
break;
|
||||
}
|
||||
exitFunction();
|
||||
return success;
|
||||
}
|
||||
@ -16,9 +16,8 @@
|
||||
void wait(int ticks);
|
||||
void death(char *message, u8 *buffer);
|
||||
|
||||
|
||||
int nfMain(void);
|
||||
|
||||
int nandFirmRead(void);
|
||||
bool nandFirmRead(void);
|
||||
bool nandFirmImport(bool sdmc);
|
||||
int nandPrintInfo(void);
|
||||
bool nandPrintInfo(void);
|
||||
@ -119,10 +119,6 @@ void nandGetInfo(void) {
|
||||
|
||||
bool nandio_startup()
|
||||
{
|
||||
if (!nand_Startup())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nand_ReadSectors(0, 1, sector_buf);
|
||||
is3DS = parse_ncsd(sector_buf) == 0;
|
||||
@ -250,6 +246,7 @@ bool nandio_write_sectors(sec_t offset, sec_t len, const void *buffer)
|
||||
}
|
||||
|
||||
bool good_nandio_write(int inputAddress, int inputLength, u8 *buffer, bool crypt) {
|
||||
nandWritten = true;
|
||||
// Sorry lol I just don't want to deal with sector calculation
|
||||
int byteOffset = inputAddress % SECTOR_SIZE;
|
||||
int sectorNum = inputAddress / SECTOR_SIZE;
|
||||
@ -310,6 +307,7 @@ bool good_nandio_write(int inputAddress, int inputLength, u8 *buffer, bool crypt
|
||||
}
|
||||
|
||||
bool good_nandio_write_file(int inputAddress, int inputLength, FILE *fp, bool crypt) {
|
||||
nandWritten = true;
|
||||
int byteOffset = inputAddress % SECTOR_SIZE;
|
||||
int sectorNum = inputAddress / SECTOR_SIZE;
|
||||
int byteEndOffset = (inputAddress+inputLength) % SECTOR_SIZE;
|
||||
@ -381,11 +379,11 @@ bool nandio_shutdown()
|
||||
u8 stagingLevels = sector_buf[0x10];
|
||||
u8 reservedSectors = sector_buf[0x0E];
|
||||
u16 sectorsPerFatCopy = sector_buf[0x16] | ((u16)sector_buf[0x17] << 8);
|
||||
/*
|
||||
/*
|
||||
iprintf("[i] Staging for %i FAT copies\n",stagingLevels);
|
||||
iprintf("[i] Stages starting at %i\n",reservedSectors);
|
||||
iprintf("[i] %i sectors per stage\n",sectorsPerFatCopy);
|
||||
*/
|
||||
*/
|
||||
if (stagingLevels > 1)
|
||||
{
|
||||
for (u32 sector = 0;sector < sectorsPerFatCopy; sector++)
|
||||
|
||||
@ -450,7 +450,7 @@ bool nitroFSGood(void) {
|
||||
|
||||
fread(version, 1, length, file);
|
||||
|
||||
printf("VER |%s|%s|\n", version, VERSION);
|
||||
//printf("VER |%s|%s|\n", version, VERSION);
|
||||
nitroFSGood = (strcmp(version, VERSION) == 0);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
BIN
nitrofiles/import/common/HWINFO_N.dat
Executable file
BIN
nitrofiles/import/common/HWINFO_N.dat
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user