package org.herac.tuxguitar.io.gpx;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GPXFileSystem {
private static final int HEADER_BCFS = 1397113666;
private static final int HEADER_BCFZ = 1514554178;
private List fileSystem;
public GPXFileSystem(){
this.fileSystem = new ArrayList();
}
public List getFileNames(){
List gpxFileNames = new ArrayList();
Iterator it = this.fileSystem.iterator();
while( it.hasNext() ){
GPXFile file = (GPXFile)it.next();
gpxFileNames.add( file.getFileName() );
}
return gpxFileNames;
}
public byte[] getFileContents( String fileName ){
Iterator it = this.fileSystem.iterator();
while( it.hasNext() ){
GPXFile file = (GPXFile)it.next();
if( file.getFileName().equals( fileName ) ){
return file.getFileContents();
}
}
return null;
}
public String getFileContentsAsString( String fileName ){
byte[] fileContents = this.getFileContents(fileName);
if( fileContents != null ){
return getString(fileContents, 0, fileContents.length);
}
return null;
}
public InputStream getFileContentsAsStream( String fileName ){
byte[] fileContents = this.getFileContents(fileName);
if( fileContents != null ){
return new ByteArrayInputStream( fileContents );
}
return null;
}
public boolean isSupportedHeader( int header ){
return (header == HEADER_BCFS || header == HEADER_BCFZ);
}
public int getHeader(InputStream in) throws Throwable{
return getInteger(in);
}
public void load(InputStream in) throws Throwable {
this.load(getHeader(in), in);
}
public void load(int header, InputStream in) throws Throwable {
this.load(header, new GPXByteBuffer( getBytes(in) ) );
}
public void load(int header, GPXByteBuffer srcBuffer) throws Throwable {
if (header == HEADER_BCFS ) {
byte[] bcfsBytes = srcBuffer.readBytes( srcBuffer.length() );
int sectorSize = 0x1000;
int offset = 0;
while ( (offset = (offset + sectorSize)) + 3 < bcfsBytes.length ) {
if (getInteger(bcfsBytes, offset) == 2) {
int indexFileName = (offset + 4);
int indexFileSize = (offset + 0x8C);
int indexOfBlock = (offset + 0x94);
int block = 0;
int blockCount = 0;
ByteArrayOutputStream fileBytesStream = new ByteArrayOutputStream();
while( (block = (getInteger(bcfsBytes, (indexOfBlock + (4 * (blockCount ++)))))) != 0 ){
fileBytesStream.write( getBytes(bcfsBytes, (offset = (block * sectorSize)), sectorSize) );
}
int fileSize = getInteger(bcfsBytes , indexFileSize);
byte[] fileBytes = fileBytesStream.toByteArray();
if ( fileBytes.length >= fileSize ){
this.fileSystem.add(new GPXFile(getString(bcfsBytes, indexFileName, 127), getBytes(fileBytes, 0, fileSize)));
}
}
}
} else if ( header == HEADER_BCFZ ) {
ByteArrayOutputStream bcfsBuffer = new ByteArrayOutputStream();
int expectLength = getInteger(srcBuffer.readBytes(4),0);
while ( !srcBuffer.end() && srcBuffer.offset() < expectLength ){
int flag = srcBuffer.readBits(1);
if (flag == 1) {
int bits = srcBuffer.readBits(4);
int offs = srcBuffer.readBitsReversed(bits);
int size = srcBuffer.readBitsReversed(bits);
byte[] bcfsBytes = bcfsBuffer.toByteArray();
int pos = ( bcfsBytes.length - offs );
for( int i = 0; i < (size > offs ? offs : size) ; i ++ ){
bcfsBuffer.write( bcfsBytes[pos + i] ) ;
}
} else {
int size = srcBuffer.readBitsReversed(2);
for(int i = 0; i < size; i ++ ){
bcfsBuffer.write( srcBuffer.readBits(8) );
}
}
}
this.load( new ByteArrayInputStream( bcfsBuffer.toByteArray() ) );
} else {
throw new Exception("This is not a GPX file");
}
}
private int getInteger(InputStream in) throws Throwable{
byte[] bytes = new byte[4];
in.read( bytes );
return getInteger(bytes, 0);
}
private byte[] getBytes(InputStream in) throws Throwable {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int read = 0;
while((read = in.read()) != -1){
out.write(read);
}
byte[] bytes = out.toByteArray();
in.close();
out.close();
out.flush();
return bytes;
}
private int getInteger(byte[] source, int offset) {
byte[] bytes = new byte[4];
bytes[0] = source[ offset + 0];
bytes[1] = source[ offset + 1];
bytes[2] = source[ offset + 2];
bytes[3] = source[ offset + 3];
return ((bytes[3] & 0xff) << 24) | ((bytes[2] & 0xff) << 16) | ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff);
}
private byte[] getBytes(byte[] source, int offset, int length ){
byte[] bytes = new byte[length];
for( int i = 0 ; i < length ; i ++ ){
if( source.length > offset + i ){
bytes[i] = source[ offset + i ];
}
}
return bytes;
}
private String getString(byte[] source, int offset, int length ){
int charsLength = 0;
char[] chars = new char[length];
for( int i = 0 ; i < length ; i ++ ){
int charValue = ((source[ offset + i ]) & 0xff);
if( charValue == 0 ){
break;
}
chars[i] = (char)charValue;
charsLength = (i + 1);
}
char[] string = new char[ charsLength ];
for( int i = 0 ; i < charsLength ; i ++ ){
string[i] = chars[i];
}
return new String( string );
}
private class GPXFile {
private String fileName;
private byte[] fileContents;
public GPXFile(String fileName, byte[] fileContents){
this.fileName = fileName;
this.fileContents = fileContents;
}
public String getFileName() {
return this.fileName;
}
public byte[] getFileContents() {
return this.fileContents;
}
}
}