usbloadergx/source/usbloader/wbfs_fat.c
e.bovendeur 2543c555a4 Lots of changes in this revision:
* Added MEM2 support by Hibern
* Better partition support (by oggzee)
* Support for subdirectories in FAT32 (by oggzee)
* Added support for cios 223 and 250
* Added BCA support (go to Settings->Custom Paths) to change the path of the BCA files (by Hermes)
* Fixed issue with hairless mode
* Fixed issue with IOS_ReloadIOSsafe (by giantpune)
* Added setting to save games in a subdirectory
* Fixed slow startup when loading from FAT (WiiTDB required!)
* Changed handling of new titles a bit (speed improvement)

Known issue:
* FAT rename and re-id broken again due to subdirectory support (yes, I'm lazy)
2009-12-04 15:05:20 +00:00

562 lines
12 KiB
C

#include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include <ogcsys.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/statvfs.h>
#include <ctype.h>
#include "libwbfs/libwbfs.h"
#include "sdhc.h"
#include "usbstorage.h"
#include "utils.h"
#include "video.h"
#include "wbfs.h"
#include "wdvd.h"
#include "splits.h"
#include "fat.h"
#include "partition_usbloader.h"
#include "wpad.h"
#include "wbfs_fat.h"
#include "disc.h"
#include "settings/cfg.h"
// WBFS FAT by oggzee
// max fat fname = 256
#define MAX_FAT_PATH 1024
#define D_S(A) A, sizeof(A)
char wbfs_fat_drive[16];
char wbfs_fat_dir[16] = "/wbfs";
int wbfs_fat_vfs_have = 0;
int wbfs_fat_vfs_lba = 0;
struct statvfs wbfs_fat_vfs;
split_info_t split;
static u32 fat_sector_size = 512;
void WBFS_Spinner(s32 x, s32 max);
s32 __WBFS_ReadDVD(void *fp, u32 lba, u32 len, void *iobuf);
s32 _WBFS_FAT_GetHeadersCount(void *outbuf, u32 *count, u32 len)
{
DIR *dir;
struct dirent *dent;
char *p;
int ret, cnt = 0;
char path[MAX_FAT_PATH];
wbfs_t *part = NULL;
u32 size;
u8 *ptr;
struct discHdr tmpHdr;
int hdrsize;
struct stat st;
u8 id[8];
int is_dir;
//dbg_time1();
strcpy(path, wbfs_fat_drive);
strcat(path, wbfs_fat_dir);
dir = opendir(path);
//printf("opendir: %s %p\n", path, dir); Wpad_WaitButtons();
if (!dir) {
*count = 0;
return 0;
}
while ((dent = readdir(dir)) != NULL) {
//printf("found: %s\n", dent->d_name);
if (outbuf && cnt >= *count) break;
if ((char)dent->d_name[0] == '.') continue;
if (strlen(dent->d_name) < 8) continue; // "GAMEID_x"
memcpy(id, dent->d_name, 6);
id[6] = 0;
strcpy(path, wbfs_fat_drive);
strcat(path, wbfs_fat_dir);
strcat(path, "/");
strcat(path, dent->d_name);
stat(path, &st);
is_dir = S_ISDIR(st.st_mode);
//printf("path: %s %d\n", path, is_dir);
if (is_dir) {
// usb:/wbfs/GAMEID_TITLE/GAMEID.wbfs
if (dent->d_name[6] != '_') continue;
strcat(path, "/");
strcat(path, (char*)id);
strcat(path, ".wbfs");
//printf("path2: %s\n", path);
if (stat(path, &st) == -1) continue;
} else {
// usb:/wbfs/GAMEID.wbfs
p = strrchr(dent->d_name, '.');
if (!p) continue;
if (strcasecmp(p, ".wbfs") != 0) continue;
if (strlen(dent->d_name) != 11) continue; // GAMEID.wbfs
}
//printf("found: %s %d MB\n", path, (int)(st.st_size/1024/1024));
// size must be at least 1MB to be considered a valid wbfs file
if (st.st_size < 1024*1024) continue;
if (!outbuf) {
// just counting
cnt++;
continue;
}
ptr = ((u8 *)outbuf) + (cnt * len);
hdrsize = len;
// if we have titles.txt entry use that
char *title = cfg_get_title(id);
// if directory, and no titles.txt get title from dir name
if (!title && is_dir) {
title = &dent->d_name[7];
}
if (title) {
memset(&tmpHdr, 0, sizeof(tmpHdr));
memcpy(tmpHdr.id, id, 6);
strncpy(tmpHdr.title, title, sizeof(tmpHdr.title)-1);
tmpHdr.magic = 0x5D1C9EA3;
memcpy(ptr, &tmpHdr, hdrsize);
cnt++;
continue;
}
// else read it from wbfs file directly
FILE *fp = fopen(path, "rb");
if (fp != NULL) {
fseek(fp, 512, SEEK_SET);
fread(&tmpHdr, sizeof(struct discHdr), 1, fp);
fclose(fp);
if ((tmpHdr.magic == 0x5D1C9EA3) && (memcmp(tmpHdr.id, id, 6) == 0)) {
memcpy(ptr, &tmpHdr, hdrsize);
cnt++;
continue;
}
}
// no title found, read it from wbfs file
// but this is a little bit slower
// open 'partition' file
part = WBFS_FAT_OpenPart(id);
if (!part) {
printf("bad wbfs file: %s\n", dent->d_name);
sleep(2);
continue;
}
/* Get header */
ret = wbfs_get_disc_info(part, 0, ptr, hdrsize, &size);
if (ret == 0) cnt++;
WBFS_FAT_ClosePart(part);
}
*count = cnt;
closedir(dir);
//dbg_time2("\nFAT HDRS");
//Wpad_WaitButtonsCommon();
return 0;
}
s32 WBFS_FAT_GetCount(u32 *count)
{
*count = 0;
_WBFS_FAT_GetHeadersCount(NULL, count, 0);
return 0;
}
s32 WBFS_FAT_GetHeaders(void *outbuf, u32 cnt, u32 len)
{
_WBFS_FAT_GetHeadersCount(outbuf, &cnt, len);
return 0;
}
wbfs_disc_t* WBFS_FAT_OpenDisc(u8 *discid)
{
wbfs_t *part = WBFS_FAT_OpenPart(discid);
if (!part) return NULL;
return wbfs_open_disc(part, discid);
}
void WBFS_FAT_CloseDisc(wbfs_disc_t* disc)
{
if (!disc) return;
wbfs_t *part = disc->p;
wbfs_close_disc(disc);
WBFS_FAT_ClosePart(part);
return;
}
s32 WBFS_FAT_DiskSpace(f32 *used, f32 *free)
{
f32 size;
int ret;
*used = 0;
*free = 0;
// statvfs is slow, so cache values
if (!wbfs_fat_vfs_have || wbfs_fat_vfs_lba != wbfs_part_lba) {
ret = statvfs(wbfs_fat_drive, &wbfs_fat_vfs);
if (ret) return 0;
wbfs_fat_vfs_have = 1;
wbfs_fat_vfs_lba = wbfs_part_lba;
}
/* FS size in GB */
size = (f32)wbfs_fat_vfs.f_frsize * (f32)wbfs_fat_vfs.f_blocks / GB_SIZE;
*free = (f32)wbfs_fat_vfs.f_frsize * (f32)wbfs_fat_vfs.f_bfree / GB_SIZE;
*used = size - *free;
return 0;
}
static int nop_read_sector(void *_fp,u32 lba,u32 count,void*buf)
{
//printf("read %d %d\n", lba, count); //Wpad_WaitButtons();
return 0;
}
static int nop_write_sector(void *_fp,u32 lba,u32 count,void*buf)
{
//printf("write %d %d\n", lba, count); //Wpad_WaitButtons();
return 0;
}
void WBFS_FAT_fname(u8 *id, char *fname, int len, char *path)
{
if (path == NULL) {
snprintf(fname, len, "%s%s/%.6s.wbfs", wbfs_fat_drive, wbfs_fat_dir, id);
} else {
snprintf(fname, len, "%s/%.6s.wbfs", path, id);
}
}
void mk_gameid_title(struct discHdr *header, char *name, int re_space)
{
int i, len;
memcpy(name, header->id, 6);
name[6] = 0;
strcat(name, "_");
strcat(name, get_title(header));
// replace silly chars with '_'
len = strlen(name);
for (i = 0; i < len; i++) {
if(strchr("\\/:<>|\"", name[i]) || iscntrl(name[i])) {
name[i] = '_';
}
if(re_space && name[i]==' ') {
name[i] = '_';
}
}
}
void WBFS_FAT_get_dir(struct discHdr *header, char *path)
{
strcpy(path, wbfs_fat_drive);
strcat(path, wbfs_fat_dir);
if (Settings.FatInstallToDir) {
strcat(path, "/");
mk_gameid_title(header, path + strlen(path), 0);
}
}
void mk_title_txt(struct discHdr *header, char *path)
{
char fname[MAX_FAT_PATH];
FILE *f;
strcpy(fname, path);
strcat(fname, "/");
mk_gameid_title(header, fname+strlen(fname), 1);
strcat(fname, ".txt");
f = fopen(fname, "wb");
if (!f) return;
fprintf(f, "%.6s = %.64s\n", header->id, get_title(header));
fclose(f);
printf("Info file: %s\n", fname);
}
int WBFS_FAT_find_fname(u8 *id, char *fname, int len)
{
struct stat st;
WBFS_FAT_fname(id, fname, len, NULL);
if (stat(fname, &st) == 0) return 1;
// direct file not found, check subdirs
DIR *dir;
struct dirent *dent;
char path[MAX_FAT_PATH];
strcpy(path, wbfs_fat_drive);
strcat(path, wbfs_fat_dir);
dir = opendir(path);
//printf("opendir: %s %p\n", path, dir); Wpad_WaitButtons();
if (!dir) {
goto err;
}
while ((dent = readdir(dir)) != NULL) {
char *name = (char*)dent->d_name;
if (name[0] == '.') continue;
if (name[6] != '_') continue;
if (strncmp(name, (char*)id, 6) != 0) continue;
if (strlen(name) < 8) continue;
snprintf(fname, len, "%s/%s/%.6s.wbfs", path, name, id);
if (stat(fname, &st) == 0) {
closedir(dir);
return 2;
}
}
closedir(dir);
// not found
err:
*fname = 0;
return 0;
}
wbfs_t* WBFS_FAT_OpenPart(u8 *id)
{
char fname[MAX_FAT_PATH];
wbfs_t *part = NULL;
int ret;
// wbfs 'partition' file
if ( !WBFS_FAT_find_fname(id, fname, sizeof(fname)) ) return NULL;
ret = split_open(&split, fname);
if (ret) return NULL;
part = wbfs_open_partition(
split_read_sector,
nop_write_sector, //readonly //split_write_sector,
&split, fat_sector_size, split.total_sec, 0, 0);
if (!part) {
split_close(&split);
}
return part;
}
wbfs_t* WBFS_FAT_CreatePart(u8 *id, char *path)
{
char fname[MAX_FAT_PATH];
wbfs_t *part = NULL;
u64 size = (u64)143432*2*0x8000ULL;
u32 n_sector = size / 512;
int ret;
//printf("CREATE PART %s %lld %d\n", id, size, n_sector);
snprintf(D_S(fname), "%s%s", wbfs_fat_drive, wbfs_fat_dir);
mkdir(fname, 0777); // base usb:/wbfs
mkdir(path, 0777); // game subdir
WBFS_FAT_fname(id, fname, sizeof(fname), path);
printf("Writing to %s\n", fname);
ret = split_create(&split, fname, OPT_split_size, size, true);
if (ret) return NULL;
// force create first file
u32 scnt = 0;
int fd = split_get_file(&split, 0, &scnt, 0);
if (fd<0) {
printf("ERROR creating file\n");
sleep(2);
split_close(&split);
return NULL;
}
part = wbfs_open_partition(
split_read_sector,
split_write_sector,
&split, fat_sector_size, n_sector, 0, 1);
if (!part) {
split_close(&split);
}
return part;
}
void WBFS_FAT_ClosePart(wbfs_t* part)
{
if (!part) return;
split_info_t *s = (split_info_t*)part->callback_data;
wbfs_close(part);
if (s) split_close(s);
}
s32 WBFS_FAT_RemoveGame(u8 *discid)
{
char fname[MAX_FAT_PATH];
int loc;
// wbfs 'partition' file
loc = WBFS_FAT_find_fname(discid, fname, sizeof(fname));
if ( !loc ) return -1;
split_create(&split, fname, 0, 0, true);
split_close(&split);
if (loc == 1) return 0;
// game is in subdir
// remove optional .txt file
DIR *dir;
struct dirent *dent;
char path[MAX_FAT_PATH];
strncpy(path, fname, sizeof(path));
char *p= strrchr(path, '/');
if (p) *p = 0;
dir = opendir(path);
if (!dir) return 0;
while ((dent = readdir(dir)) != NULL) {
char *name = (char*)dent->d_name;
if (name[0] == '.') continue;
if (name[6] != '_') continue;
if (strncmp(name, (char*)discid, 6) != 0) continue;
p = strrchr(name, '.');
if (!p) continue;
if (strcasecmp(p, ".txt") != 0) continue;
snprintf(fname, sizeof(fname), "%s/%s", path, name);
remove(fname);
break;
}
closedir(dir);
// remove game subdir
//rmdir(path);
if (unlink(path) == -1) {
}
return 0;
}
s32 WBFS_FAT_AddGame(void)
{
static struct discHdr header ATTRIBUTE_ALIGN(32);
char path[MAX_FAT_PATH];
wbfs_t *part = NULL;
s32 ret;
// read ID from DVD
Disc_ReadHeader(&header);
// path
WBFS_FAT_get_dir(&header, path);
// create wbfs 'partition' file
part = WBFS_FAT_CreatePart(header.id, path);
if (!part) return -1;
/* Add game to device */
partition_selector_t part_sel = ONLY_GAME_PARTITION;
int copy_1_1 = 0;
/*
switch (CFG.install_partitions) {
case CFG_INSTALL_GAME:
part_sel = ONLY_GAME_PARTITION;
break;
case CFG_INSTALL_ALL:
part_sel = ALL_PARTITIONS;
break;
case CFG_INSTALL_1_1:
part_sel = ALL_PARTITIONS;
copy_1_1 = 1;
break;
}
*/
extern wbfs_t *hdd;
wbfs_t *old_hdd = hdd;
hdd = part; // used by spinner
ret = wbfs_add_disc(part, __WBFS_ReadDVD, NULL, WBFS_Spinner, part_sel, copy_1_1);
hdd = old_hdd;
wbfs_trim(part);
WBFS_FAT_ClosePart(part);
if (ret < 0) return ret;
mk_title_txt(&header, path);
return 0;
}
s32 WBFS_FAT_DVD_Size(u64 *comp_size, u64 *real_size)
{
s32 ret;
u32 comp_sec = 0, last_sec = 0;
wbfs_t *part = NULL;
u64 size = (u64)143432*2*0x8000ULL;
u32 n_sector = size / fat_sector_size;
u32 wii_sec_sz;
// init a temporary dummy part
// as a placeholder for wbfs_size_disc
part = wbfs_open_partition(
nop_read_sector, nop_write_sector,
NULL, fat_sector_size, n_sector, 0, 1);
if (!part) return -1;
wii_sec_sz = part->wii_sec_sz;
/* Add game to device */
partition_selector_t part_sel = ONLY_GAME_PARTITION;
/*
if (CFG.install_partitions) {
part_sel = ALL_PARTITIONS;
} else {
part_sel = ONLY_GAME_PARTITION;
}
*/
ret = wbfs_size_disc(part, __WBFS_ReadDVD, NULL, part_sel, &comp_sec, &last_sec);
wbfs_close(part);
if (ret < 0)
return ret;
*comp_size = (u64)wii_sec_sz * comp_sec;
*real_size = (u64)wii_sec_sz * last_sec;
return 0;
}
s32 WBFS_FAT_RenameGame(u8 *discid, const void *newname)
{
wbfs_t *part = WBFS_FAT_OpenPart(discid);
if (!part)
return -1;
s32 ret = wbfs_ren_disc(part, discid,(u8*)newname);
WBFS_FAT_ClosePart(part);
return ret;
}
s32 WBFS_FAT_ReIDGame(u8 *discid, const void *newID)
{
wbfs_t *part = WBFS_FAT_OpenPart(discid);
if (!part)
return -1;
s32 ret = wbfs_rID_disc(part, discid,(u8*)newID);
WBFS_FAT_ClosePart(part);
if(ret == 0)
{
char fname[100];
char fnamenew[100];
s32 cnt = 0x31;
WBFS_FAT_fname(discid, fname, sizeof(fname), NULL);
WBFS_FAT_fname((u8*) newID, fnamenew, sizeof(fnamenew), NULL);
int stringlength = strlen(fname);
while(rename(fname, fnamenew) == 0)
{
fname[stringlength] = cnt;
fname[stringlength+1] = 0;
fnamenew[stringlength] = cnt;
fnamenew[stringlength+1] = 0;
cnt++;
}
}
return ret;
}