package tk.captainsplexx.Resource.TOC;
import java.nio.ByteOrder;
import java.util.ArrayList;
import tk.captainsplexx.Resource.FileHandler;
import tk.captainsplexx.Resource.FileSeeker;
public class TocManager {
public static enum TocFieldType {
STRING, BOOL, INTEGER, LONG, GUID, SHA1, LIST, RAW, RAW2,
ENTRY // ENTRY IS ONLY FOR RECREATION!
};
public static enum TocEntryType {
ORDINARY, UNKNOWN
};
public static enum TocFileType {
XORSig, Sig, None, SbPart
};
/*ONLY NEEDED IF RAW FILE SHOULD SHOW UP IN EXPLORER. (DEBUG)
public static ConvertedTocFile getConvertedToc(byte[] toc){
return TocConverter.convertTocFile(readToc(toc));
}
*/
public static TocFile readToc(String path){
byte[] rawData = FileHandler.readFile(path+".toc");
return readToc(rawData, path+".sb");
}
public static TocFile readToc(byte[] fileArray, String sbPath) {
FileSeeker seeker = new FileSeeker();
ArrayList<TocEntry> entries = new ArrayList<TocEntry>();
byte[] data = null;
int header = FileHandler.readInt(fileArray, seeker, ByteOrder.BIG_ENDIAN);
TocFileType fileType = null;
if (header == 0x00D1CE00 || header == 0x00D1CE01) { //#the file is XOR encrypted and has a signature
fileType = TocFileType.XORSig;
data = null;
System.out.println("TODO: XOR Decryption with TOC Files!"); //TODO
/*
* seek(296) #skip the signature
* key=[ord(f.read(1))^123 for i in xrange(260)] #bytes 257 258 259 are not used; XOR the key with 123 right away
* encryptedData=f.read()
* data="".join([chr(key[i%257]^ord(encryptedData[i])) for i in xrange(len(encryptedData))]) #go through the data applying one key byte on one data
*/
} else if (header == 0x00D1CE03) {
fileType = TocFileType.Sig;
data = new byte[fileArray.length-556];
for (int i = 0; i < data.length; i++){
data[i] = fileArray[i+556]; //seek(556) #skip signature + skip empty key + skip header
}
} else { //#the file is not encrypted; no key + no signature
fileType = TocFileType.None;
data = fileArray;
}
seeker = new FileSeeker();
if (data!=null){
while (seeker.getOffset() < data.length){ //READ ENTRIES
TocEntry entry = readEntry(data, seeker);
if (entry != null){
entries.add(entry);
}
}//EOF
TocFile file = new TocFile(fileType, sbPath);
file.getEntries().addAll(entries);
return file;
}else{
return null;
}
}
public static TocFile readSbPart(byte[] part) { // instead of reading the complete file, only read parts
FileSeeker seeker = new FileSeeker();
ArrayList<TocEntry> entries = new ArrayList<TocEntry>();
while (seeker.getOffset() < part.length){ //READ ENTRIES
TocEntry entry = readEntry(part, seeker);
if (entry != null){
entries.add(entry);
}
}//EOF
TocFile file = new TocFile(TocFileType.SbPart);
file.getEntries().addAll(entries);
return file;
}
static TocEntry readEntry(byte[] data, FileSeeker seeker){
//System.out.println("Reading ENTRY at "+seeker.getOffset());
TocEntry entry;
int entryType = (FileHandler.readByte(data, seeker) & 0xFF); //byte needs to be casted to unsigned.
if (entryType == 0x82){
entry = new TocEntry(TocEntryType.ORDINARY);
int entrySize = FileHandler.readLEB128(data, seeker);
int entryOffset = seeker.getOffset();
while (seeker.getOffset() < entryOffset+entrySize){ //READ FIELDS
TocField field = readField(data, seeker);
if (field != null){
entry.getFields().add(field);
}
}
}else{
if (entryType != 0){
entry = new TocEntry(TocEntryType.UNKNOWN);
System.err.println("Unknown Type in TocManger detected: "+entryType+" at "+seeker.getOffset());
}else{
entry = null;
}
//TODO
}
return entry;
}
static TocField readField(byte[] data, FileSeeker seeker){
TocField field = null;
int fieldType = (FileHandler.readByte(data, seeker) & 0xFF); //byte needs to be casted to unsigned.
String name = "";
if (fieldType != 0x00){
name = FileHandler.readString(data, seeker);
}
/* Really stupid stuff happend here, i have forgotten to replace the original path with the new one and so i currupted the original ones :/
* if (name.equalsIgnoreCase("sto")){
* System.err.println("DEBUG");
}*/
if (fieldType == 0x01){ //#list type, containing ENTRIES (MULTIPLE ONE)
ArrayList<TocEntry> list = new ArrayList<TocEntry>();
int listSize = FileHandler.readLEB128(data, seeker);
int listOffset = seeker.getOffset();
while (seeker.getOffset() < listOffset+listSize){
list.add(readEntry(data, seeker));
}
if (list.isEmpty()){list = null;}
field = new TocField(list, TocFieldType.LIST, name);
}else if (fieldType == 0x0F){ //ID 16 stored as HEXSTRING
field = new TocField(FileHandler.bytesToHex(FileHandler.readByte(data, seeker, 16)), TocFieldType.GUID, name);
}else if (fieldType == 0x09){ //LONG
field = new TocField(FileHandler.readLong(data, seeker), TocFieldType.LONG, name);
}else if (fieldType == 0x08){ //INTEGER
field = new TocField(FileHandler.readInt(data, seeker), TocFieldType.INTEGER, name);
}else if (fieldType == 0x06){ //BOOL
boolean bool = false;
if (FileHandler.readByte(data, seeker) == 0x01){
bool = true;
}
field = new TocField(bool, TocFieldType.BOOL, name);
}else if (fieldType == 0x02){
int length = FileHandler.readLEB128(data, seeker);
field = new TocField(FileHandler.readByte(data, seeker, length), TocFieldType.RAW, name);
}else if (fieldType == 0x13){
int length = FileHandler.readLEB128(data, seeker);
field = new TocField(FileHandler.readByte(data, seeker, length), TocFieldType.RAW2, name);
//sbTocFile -> RES -> ENTRY: idata 0x13 //TODO What is the diffrence ?
}else if (fieldType == 0x10){ //SHA1 stored as HEXSTRING
field = new TocField(FileHandler.bytesToHex(FileHandler.readByte(data, seeker, 20)), TocFieldType.SHA1, name);
}else if (fieldType == 0x07){ // #string, length (including trailing null) prefixed as 7bit int
FileHandler.readLEB128(data, seeker); //SKIP LENGTH
field = new TocField(FileHandler.readString(data, seeker), TocFieldType.STRING, name);
}else if (fieldType == 0x00){
return null;
}else{
System.err.println("Unknown FieldType: "+fieldType+" in TocManager detected at "+seeker.getOffset());
}
return field;
}
}