mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-22 19:29:18 +01:00
314 lines
7.4 KiB
C
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 - (32 * 1024);
|
|
// 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;
|
|
}
|
|
|