package tk.captainsplexx.Resource.CAS;
import java.nio.ByteOrder;
import java.util.ArrayList;
import tk.captainsplexx.Game.Core;
import tk.captainsplexx.Game.Game;
import tk.captainsplexx.Maths.LZ4;
import tk.captainsplexx.Maths.Patcher;
import tk.captainsplexx.Resource.FileHandler;
import tk.captainsplexx.Resource.FileSeeker;
import tk.captainsplexx.Resource.ResourceHandler;
public class CasDataReader { //casPath == folderPath
public static byte[] readCas(String baseSHA1, String deltaSHA1, String SHA1, Integer patchType){
return readCas(baseSHA1, deltaSHA1, SHA1, patchType, null);
}
public static byte[] readCas(String baseSHA1, String deltaSHA1, String SHA1, Integer patchType, ResourceHandler resHandler){
ResourceHandler rs = null;
if (resHandler==null){
Game game = Core.getGame();
rs = game.getResourceHandler();
}else{
rs = resHandler;
}
if (patchType == 666){
System.err.println("'UNKNOWN SOURCE' search started!");
//Unknown
byte[] data = CasDataReader.readCas(SHA1, Core.gamePath+"/Update/Patch/Data", rs.getPatchedCasCatManager().getEntries(), false);
if (data != null){
System.out.println("SHA1 was found in patched CasCat!");
return data;
}else{
byte[] data2 = CasDataReader.readCas(SHA1, Core.gamePath+"/Data", rs.getCasCatManager().getEntries(), false);
if (data2 != null){
System.out.println("SHA1 was found in unpatched CasCat!");
return data2;
}else{
System.err.println("SHA could not be found in any CasCat!");
return null;
}
}
}else if (patchType == 2){
//Patched using delta
byte[] base = CasDataReader.readCas(baseSHA1, Core.gamePath+"/Data", rs.getCasCatManager().getEntries(), false);
byte[] delta = CasDataReader.readCas(deltaSHA1, Core.gamePath+"/Update/Patch/Data", rs.getPatchedCasCatManager().getEntries(), true);
if (base==null||delta==null){
System.err.println("Base or Delta contains null data.");
return null;
}
byte[] data = Patcher.getPatchedData(base, delta);
if (data != null){
return data;
}else{
System.err.println("The patcher return null data. Something went wrong!");
return null;
}
}else if(patchType == 1){
//Patched using data from update cas
byte[] data = CasDataReader.readCas(SHA1, Core.gamePath+"/Update/Patch/Data", rs.getPatchedCasCatManager().getEntries(), false);
if (data != null){
return data;
}else{
return null;
}
}else{
//Unpatched
byte[] data = CasDataReader.readCas(SHA1, Core.gamePath+"/Data", rs.getCasCatManager().getEntries(), false);
if (data != null){
return data;
}else{
System.err.println("null data... in CasDataReader (unpatched data)");
return null;
}
}
}
public static byte[] readCas(String SHA1, String casFolderPath, ArrayList<CasCatEntry> casCatEntries, boolean hasNoBlockLogic){
try{
SHA1 = SHA1.replaceAll("\\s","");
for (CasCatEntry e : casCatEntries){
if (!e.getSHA1().equals(SHA1.toLowerCase())){continue;}
String casFile = "";
if (e.getCasFile()<10){casFile+="0";}
casFile += e.getCasFile() + "";
String casFilePath = casFolderPath;
if (!casFilePath.endsWith("/")){casFilePath+="/";}
casFilePath += "cas_"+ casFile + ".cas";
System.out.println("Reading CAS: "+ casFilePath+" for SHA1: "+SHA1);
if (!hasNoBlockLogic){//hasBlockLogic :)
if (e.getProcSize()>=0x010000){
System.out.println("WARNING: Decompress each block and glue the decompressed parts together to obtain the file.");
}
return convertToRAWData(FileHandler.readFile(casFilePath, e.getOffset(), e.getProcSize()));
}else{
return FileHandler.readFile(casFilePath, e.getOffset(), e.getProcSize());
}
}
System.err.println("SHA "+SHA1+" not found in "+casFolderPath);
return null;
}catch (NullPointerException e){
e.printStackTrace();
return null;
}
}
public static byte[] convertToRAWData(byte[] data){
FileSeeker seeker = new FileSeeker("CasDataReader");
ArrayList<Byte> output = new ArrayList<Byte>();
while(seeker.getOffset()<data.length){
byte[] decompressedBlock = readBlock(data, seeker);
if (decompressedBlock != null && !seeker.hasError()){
for (Byte b : decompressedBlock){
output.add(b);
/*DEBUG
if (output.size()==1398120){
System.out.println(seeker.getOffset());
}*/
}
}else{
System.err.println("CasDataReader was not able to decode Block! - Following operations will fail!");
return null;
}
}//End of InputStream
return FileHandler.convertFromList(output);
}
public static byte[] readBlock(byte[] encodedEntry, FileSeeker seeker){
//FileHandler.writeFile("output/readBlock", encodedEntry);
int decompressedSize = FileHandler.readInt(encodedEntry, seeker, ByteOrder.BIG_ENDIAN);
int compressionType = FileHandler.readShort(encodedEntry, seeker, ByteOrder.BIG_ENDIAN);
int compressedSize = FileHandler.readShort(encodedEntry, seeker, ByteOrder.BIG_ENDIAN) & 0xFFFF;
if (compressionType == 0x0970){//COMPRESSED
//rawData = LZ4.decompress(FileHandler.readByte(encodedEntry, seeker, procSize-seeker.getOffset()));
byte[] lz4data = FileHandler.readByte(encodedEntry, seeker, compressedSize);
if (lz4data==null){return null;}
byte[] rawData = LZ4.decompress(lz4data);
if (rawData.length<decompressedSize){
System.err.println("Decompressed file size does not match the cas.cat given one. "+rawData.length+" of "+decompressedSize+" Bytes loaded.");
}
return rawData;
}else if (compressionType == 0x0070 || compressionType == 0x0071){//UNCOMPRESSED
if (compressedSize==0x0){
compressedSize = decompressedSize;
}
//return FileHandler.readByte(encodedEntry, seeker, procSize-seeker.getOffset());
return FileHandler.readByte(encodedEntry, seeker, compressedSize);
}else if (compressionType == 0x0){ // 0x0000 - emty payload
//seeker.setOffset(seeker.getOffset()-2); //NULL compressionSize
System.err.println("CasDataReader needs some help. 0x0000 emty payload"); //TODO
//return FileHandler.readByte(encodedEntry, seeker, compressedSize);
return null;
}else if (compressionType == 0x0270){
System.err.println("'Dragon Age Inquisition' is not supported yet. Please be patient! \n If you know the Compression type of it, let me know via twittah ;)");
return null;
}else{
System.err.println("Compression type " + FileHandler.bytesToHex(FileHandler.toBytes(compressionType, ByteOrder.LITTLE_ENDIAN))+" is not defined in CasDataReader.");
FileHandler.writeFile("output/debug/error_readBlock.tmp", encodedEntry);
return null;
}
}
}