usbloadergx/source/usbloader/splits.c
e.bovendeur a09abe355f * Added FAT support (Issue 1054)
* Fixed issue 1058
* Menus splitted to several smaller files, to reduce compile time

This version has FAT support. You can change the used partition in the game load options. Default WBFS will be used, if found. Otherwise the first FAT partition with games will be used. FAT will only work when using Hermes cios (222/223)!!!
2009-11-15 19:52:58 +00:00

314 lines
7.4 KiB
C

#include <ogcsys.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "splits.h"
#define off64_t off_t
#define FMT_llu "%llu"
#define FMT_lld "%lld"
#define split_error(x) do { /* gprintf("\nsplit error: %s\n\n",x); */ } while(0)
// 1 sector less than 4gb
//u64 OPT_split_size = (u64)4LL * 1024 * 1024 * 1024 - 512;
// 1 sector less than 2gb
u64 OPT_split_size = (u64)2LL * 1024 * 1024 * 1024 - 512;
//split_info_t split;
void split_get_fname(split_info_t *s, int idx, char *fname)
{
strcpy(fname, s->fname);
if (idx == 0 && s->create_mode) {
strcat(fname, ".tmp");
} else if (idx > 0) {
char *c = fname + strlen(fname) - 1;
*c = '0' + idx;
}
}
int split_open_file(split_info_t *s, int idx)
{
int fd = s->fd[idx];
if (fd>=0) return fd;
char fname[1024];
split_get_fname(s, idx, fname);
//char *mode = s->create_mode ? "wb+" : "rb+";
int mode = s->create_mode ? (O_CREAT | O_RDWR) : O_RDWR ;
//printf("SPLIT OPEN %s %s %d\n", fname, mode, idx); //Wpad_WaitButtons();
//f = fopen(fname, mode);
fd = open(fname, mode);
if (fd<0) return -1;
if (idx > 0 && s->create_mode) {
printf("%s Split: %d %s \n",
s->create_mode ? "Create" : "Read",
idx, fname);
}
s->fd[idx] = fd;
return fd;
}
// faster as it uses larger chunks than ftruncate internally
int write_zero(int fd, off_t size)
{
int buf[0x4000]; //64kb
int chunk;
int ret;
memset(buf, 0, sizeof(buf));
while (size) {
chunk = size;
if (chunk > sizeof(buf)) chunk = sizeof(buf);
ret = write(fd, buf, chunk);
//printf("WZ %d %d / %lld \n", ret, chunk, size);
size -= chunk;
if (ret < 0) return ret;
}
return 0;
}
int split_fill(split_info_t *s, int idx, u64 size)
{
int fd = split_open_file(s, idx);
off64_t fsize = lseek(fd, 0, SEEK_END);
if (fsize < size) {
//printf("TRUNC %d "FMT_lld"\n", idx, size); Wpad_WaitButtons();
//ftruncate(fd, size);
write_zero(fd, size - fsize);
return 1;
}
return 0;
}
int split_get_file(split_info_t *s, u32 lba, u32 *sec_count, int fill)
{
int fd;
if (lba >= s->total_sec) {
fprintf(stderr, "SPLIT: invalid sector %u / %u\n", lba, (u32)s->total_sec);
return -1;
}
int idx;
idx = lba / s->split_sec;
if (idx >= s->max_split) {
fprintf(stderr, "SPLIT: invalid split %d / %d\n", idx, s->max_split - 1);
return -1;
}
fd = s->fd[idx];
if (fd<0) {
// opening new, make sure all previous are full
int i;
for (i=0; i<idx; i++) {
if (split_fill(s, i, s->split_size)) {
printf("FILL %d\n", i);
}
}
fd = split_open_file(s, idx);
}
if (fd<0) {
fprintf(stderr, "SPLIT %d: no file\n", idx);
return -1;
}
u32 sec = lba % s->split_sec; // inside file
off64_t off = (off64_t)sec * 512;
// num sectors till end of file
u32 to_end = s->split_sec - sec;
if (*sec_count > to_end) *sec_count = to_end;
if (s->create_mode) {
if (fill) {
// extend, so that read will be succesfull
split_fill(s, idx, off + 512 * (*sec_count));
} else {
// fill up so that write continues from end of file
// shouldn't be necessary, but libfat looks buggy
// and this is faster
split_fill(s, idx, off);
}
}
lseek(fd, off, SEEK_SET);
return fd;
}
int split_read_sector(void *_fp,u32 lba,u32 count,void*buf)
{
split_info_t *s = _fp;
int fd;
u64 off = lba;
off *= 512ULL;
int i;
u32 chunk;
size_t ret;
//fprintf(stderr,"READ %d %d\n", lba, count);
for (i=0; i<(int)count; i+=chunk) {
chunk = count - i;
fd = split_get_file(s, lba+i, &chunk, 1);
if (fd<0) {
fprintf(stderr,"\n\n"FMT_lld" %d %p\n",off,count,_fp);
split_error("error seeking in disc partition");
return 1;
}
//ret = fread(buf+i*512, 512ULL, chunk, f);
ret = read(fd, buf+i*512, chunk * 512);
if (ret != chunk * 512) {
fprintf(stderr, "error reading %u %u [%u] %u = %u\n",
lba, count, i, chunk, ret);
split_error("error reading disc");
return 1;
}
}
return 0;
}
int split_write_sector(void *_fp,u32 lba,u32 count,void*buf)
{
split_info_t *s = _fp;
int fd;
u64 off = lba;
off*=512ULL;
int i;
u32 chunk;
size_t ret;
//printf("WRITE %d %d %p \n", lba, count, buf);
for (i=0; i<(int)count; i+=chunk) {
chunk = count - i;
fd = split_get_file(s, lba+i, &chunk, 0);
//if (chunk != count)
// fprintf(stderr, "WRITE CHUNK %d %d/%d\n", lba+i, chunk, count);
if (fd<0 || !chunk) {
fprintf(stderr,"\n\n"FMT_lld" %d %p\n",off,count,_fp);
split_error("error seeking in disc partition");
return 1;
}
//if (fwrite(buf+i*512, 512ULL, chunk, f) != chunk) {
//printf("write %d %p %d \n", fd, buf+i*512, chunk * 512);
ret = write(fd, buf+i*512, chunk * 512);
//printf("write ret = %d \n", ret);
if (ret != chunk * 512) {
split_error("error writing disc");
return 1;
}
}
return 0;
}
void split_init(split_info_t *s, char *fname)
{
int i;
char *p;
//fprintf(stderr, "SPLIT_INIT %s\n", fname);
memset(s, 0, sizeof(*s));
for (i=0; i<MAX_SPLIT; i++) {
s->fd[i] = -1;
}
strcpy(s->fname, fname);
s->max_split = 1;
p = strrchr(fname, '.');
if (p && (strcasecmp(p, ".wbfs") == 0)) {
s->max_split = MAX_SPLIT;
}
}
void split_set_size(split_info_t *s, u64 split_size, u64 total_size)
{
s->total_size = total_size;
s->split_size = split_size;
s->total_sec = total_size / 512;
s->split_sec = split_size / 512;
}
void split_close(split_info_t *s)
{
int i;
char fname[1024];
char tmpname[1024];
for (i=0; i<s->max_split; i++) {
if (s->fd[i] >= 0) {
close(s->fd[i]);
}
}
if (s->create_mode) {
split_get_fname(s, -1, fname);
split_get_fname(s, 0, tmpname);
rename(tmpname, fname);
}
memset(s, 0, sizeof(*s));
}
int split_create(split_info_t *s, char *fname,
u64 split_size, u64 total_size, bool overwrite)
{
int i;
int fd;
char sname[1024];
int error = 0;
split_init(s, fname);
s->create_mode = 1;
// check if any file already exists
for (i=-1; i<s->max_split; i++) {
split_get_fname(s, i, sname);
if (overwrite) {
remove(sname);
} else {
fd = open(sname, O_RDONLY);
if (fd >= 0) {
fprintf(stderr, "Error: file already exists: %s\n", sname);
close(fd);
error = 1;
}
}
}
if (error) {
split_init(s, "");
return -1;
}
split_set_size(s, split_size, total_size);
return 0;
}
int split_open(split_info_t *s, char *fname)
{
int i;
u64 size = 0;
u64 total_size = 0;
u64 split_size = 0;
int fd;
split_init(s, fname);
for (i=0; i<s->max_split; i++) {
fd = split_open_file(s, i);
if (fd<0) {
if (i==0) goto err;
break;
}
// check previous size - all splits except last must be same size
if (i > 0 && size != split_size) {
fprintf(stderr, "split %d: invalid size "FMT_lld"", i, size);
goto err;
}
// get size
//fseeko(f, 0, SEEK_END);
//size = ftello(f);
size = lseek(fd, 0, SEEK_END);
// check sector alignment
if (size % 512) {
fprintf(stderr, "split %d: size ("FMT_lld") not sector (512) aligned!",
i, size);
}
// first sets split size
if (i==0) {
split_size = size;
}
total_size += size;
}
split_set_size(s, split_size, total_size);
return 0;
err:
split_close(s);
return -1;
}