// by oggzee #include #include #include #include #include #include #include #include #include #include "splits.h" #define off64_t off_t #define FMT_llu "%llu" #define FMT_lld "%lld" #define split_error(x) do { printf("\nsplit error: %s\n\n",x); } while(0) // 1 cluster less than 4gb u64 OPT_split_size = ( u64 )4LL * 1024 * 1024 * 1024 - 32 * 1024; // 1 cluster less than 2gb //u64 OPT_split_size = (u64)2LL * 1024 * 1024 * 1024 - 32 * 1024; //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; } void *ptr = ( ( u8 * )buf ) + ( i * 512 ); ret = read( fd, ptr, 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); void *ptr = ( ( u8 * )buf ) + ( i * 512 ); ret = write( fd, ptr, 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; }