/** * Baffle Project * The MIT License (MIT) Copyright (Baffle) 2015 guye * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @author guye * **/ package com.guye.baffle.decoder; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.zip.CRC32; import java.util.zip.CheckedOutputStream; import com.guye.baffle.obfuscate.ObfuscateHelper; import com.guye.baffle.obfuscate.ObfuscateHelper.ObfuscateData; import com.guye.baffle.util.LEDataOutputStream; public class ArscFileGenerator { private ArscData arscData; private ObfuscateData obfuscateData; private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); public ArscFileGenerator(ArscData arscData, ObfuscateData obfuscateData) { this.arscData = arscData; this.obfuscateData = obfuscateData; } private StringBlock createStrings(StringBlock orgTableStrings, boolean isTableString) { try { ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); LEDataOutputStream dataOutputStream = new LEDataOutputStream( arrayOutputStream); int count = orgTableStrings.getCount(); int curOffset = 0; int[] offset = new int[count]; String newStr; byte[] strData; byte[] l = new byte[2]; byte[] l1 = new byte[2]; int offsetLen = 1; int offsetDataLen = 1; for (int i = 0; i < count; i++) { if (isTableString) { newStr = getNewTableString(orgTableStrings.getString(i)); } else { newStr = getNewKeyString(orgTableStrings.getString(i)); } strData = newStr.getBytes(UTF_8_CHARSET); offset[i] = curOffset; if (newStr.length() < 128) { offsetLen = 1; l[0] = (byte) (0x7f & (newStr.length())); } else { offsetLen = 2; short len = (short) (newStr.length()); l[0] = (byte) ((byte) ((len & 0xff00) >> 8) | 0x80); l[1] = (byte) (len & 0x00ff); } if (strData.length < 128) { l1[0] = (byte) (0x7f & (strData.length)); offsetDataLen = 1; } else { offsetDataLen = 2; short len = (short) (strData.length); l1[0] = (byte) ((byte) ((len & 0xff00) >> 8) | 0x80); l1[1] = (byte) (len & 0x00ff); } dataOutputStream.write(l, 0, offsetLen); dataOutputStream.write(l1, 0, offsetDataLen); dataOutputStream.write(strData); dataOutputStream.write(0); curOffset += (offsetLen + offsetDataLen + strData.length + 1); } strData = arrayOutputStream.toByteArray(); dataOutputStream.close(); arrayOutputStream.close(); return new StringBlock(offset, strData, orgTableStrings.getStyleOffset(), orgTableStrings.getStyle(), true); } catch (IOException e) {// not a disk IO option e.printStackTrace(); } return null; } private String getNewKeyString(String orgString) { return obfuscateData.keyMaping.get(orgString); } private String getNewTableString(String orgString) { if (orgString.startsWith(ObfuscateHelper.RES_PROFIX)) { String[] names = orgString.split("/"); if (names == null || names.length != 3) { throw new RuntimeException(); // TODO } String[] newNames = new String[3]; newNames[0] = obfuscateData.resName; newNames[1] = obfuscateData.typeMaping.get(names[1]); if (newNames[1] == null) { throw new RuntimeException(); // TODO } int index = names[2].indexOf('.'); String postfix = ""; if (index > 0) { postfix = names[2].substring(index, names[2].length()); names[2] = names[2].substring(0, index); } newNames[2] = obfuscateData.keyMaping.get(names[2]); if (newNames[2] == null) { throw new RuntimeException(); // TODO } String newString = new StringBuilder().append(newNames[0]) .append('/').append(newNames[1]).append('/') .append(newNames[2]).append(postfix).toString(); return newString; } else { return orgString; } } public CRC32 createObfuscateFile( ArscData data, StringBlock tableBlock, StringBlock keyBlock, File file ) throws IOException { FileOutputStream fileOutputStream = new FileOutputStream(file); CRC32 cksum = new CRC32(); CheckedOutputStream checkedOutputStream = new CheckedOutputStream(fileOutputStream, cksum); LEDataOutputStream out = new LEDataOutputStream(checkedOutputStream); int tableStrChange = data.getmTableStrings().getSize() - tableBlock.getSize(); int keyStrChange = data.getmSpecNames().getSize() - keyBlock.getSize(); data.getmHeader().chunkSize -=(tableStrChange + keyStrChange); data.getmHeader().write(out); out.writeInt(1); tableBlock.write(out); data.getmPkgHeader().header.chunkSize -=keyStrChange; data.getmPkgHeader().write(out); data.getTypeNames().write(out); keyBlock.write(out); byte[] buff = new byte[1024]; FileInputStream in = new FileInputStream(data.getFile()); in.skip(data.getmResIndex()); int len ; while(((len = in.read(buff)) != -1)){ out.write(buff , 0 , len); } in.close(); out.close(); checkedOutputStream.close(); fileOutputStream.close(); return cksum; } }