// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.sav; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.file.Path; import java.util.Locale; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import org.infinity.resource.Writeable; import org.infinity.resource.key.ResourceEntry; import org.infinity.util.io.ByteBufferInputStream; import org.infinity.util.io.ByteBufferOutputStream; import org.infinity.util.io.StreamUtils; /** * Specialized ResourceEntry class for compressed entries in SAV resources. */ public class SavResourceEntry extends ResourceEntry implements Writeable { private final String fileName; private int offset; private int comprLength; private int uncomprLength; private ByteBuffer cdata; public SavResourceEntry(ByteBuffer buffer, int offset) { this.offset = offset; int fileNameLength = buffer.getInt(offset); fileName = StreamUtils.readString(buffer, offset + 4, fileNameLength - 1); offset += 4 + fileNameLength; uncomprLength = buffer.getInt(offset); comprLength = buffer.getInt(offset + 4); cdata = StreamUtils.getByteBuffer(comprLength); StreamUtils.copyBytes(buffer, offset + 8, cdata, 0, comprLength); } public SavResourceEntry(ResourceEntry entry) throws Exception { comprLength = 0; uncomprLength = 0; fileName = entry.toString(); byte[] udata = StreamUtils.toArray(entry.getResourceBuffer(true)); if (udata.length == 0) { uncomprLength = 0; comprLength = 8;; udata = new byte[]{0x78, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}; } else { cdata = StreamUtils.getByteBuffer(udata.length * 2); try (DeflaterOutputStream dos = new DeflaterOutputStream(new ByteBufferOutputStream(cdata), new Deflater(Deflater.BEST_COMPRESSION))) { dos.write(udata); dos.finish(); } cdata.flip(); uncomprLength = udata.length; comprLength = cdata.limit(); // Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); // deflater.setInput(udata); // deflater.finish(); // int clength = deflater.deflate(cdata.array()); // cdata.limit(clength); // comprLength = clength; } } public int getEndOffset() { return offset + fileName.length() + 13 + cdata.limit(); } @Override public String toString() { return fileName; } @Override public String getResourceName() { return fileName; } @Override public String getExtension() { return fileName.substring(fileName.lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH); } @Override public String getTreeFolder() { return null; } @Override public boolean hasOverride() { return false; } @Override public int[] getResourceInfo(boolean ignoreoverride) throws Exception { if (fileName.toUpperCase(Locale.ENGLISH).endsWith(".TIS")) { try { ByteBuffer data = decompress(); if (!StreamUtils.readString(data, 0, 4).equalsIgnoreCase("TIS ")) { int tileCount= data.getInt(0); int tileSize = data.getInt(4); return new int[]{tileCount, tileSize}; } else { return new int[]{data.getInt(8), data.getInt(12)}; } } catch (IOException e) { e.printStackTrace(); } return null; } return new int[]{uncomprLength}; } @Override public ByteBuffer getResourceBuffer(boolean ignoreoverride) throws Exception { return decompress(); } @Override public InputStream getResourceDataAsStream(boolean ignoreoverride) throws Exception { return new ByteBufferInputStream(decompress()); } @Override public Path getActualPath(boolean ignoreoverride) { return null; } @Override public long getResourceSize(boolean ignoreOverride) { try { int[] info = getResourceInfo(); if (info != null) { if (info.length == 1) { return info[0]; } else if (info.length > 1) { return info[0]*info[1] + 0x18; } } } catch (Exception e) { e.printStackTrace(); } return -1L; } public ByteBuffer decompress() throws Exception { Inflater inflater = new Inflater(); byte udata[] = new byte[uncomprLength]; inflater.setInput(cdata.array()); inflater.inflate(udata); return StreamUtils.getByteBuffer(udata); } @Override public void write(OutputStream os) throws IOException { StreamUtils.writeInt(os, fileName.length() + 1); StreamUtils.writeString(os, fileName, fileName.length()); StreamUtils.writeByte(os, (byte)0); StreamUtils.writeInt(os, uncomprLength); StreamUtils.writeInt(os, comprLength); cdata.position(0); StreamUtils.writeBytes(os, cdata); } }