/*
* Copyright (c) 2005 (Mike) Maurice Kienenberger (mkienenb@gmail.com)
*
* 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.
*/
package org.gamenet.application.mm8leveleditor.mm6;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Map;
import org.gamenet.application.mm8leveleditor.converter.FormatConverter;
import org.gamenet.application.mm8leveleditor.converter.TGAToBMPFormatConverter;
import org.gamenet.application.mm8leveleditor.lod.LodEntry;
import org.gamenet.application.mm8leveleditor.lod.LodResource;
import org.gamenet.application.mm8leveleditor.lod.SpriteTGADataProducer;
import org.gamenet.application.mm8leveleditor.lod.TGADataProducer;
import org.gamenet.util.ByteConversions;
import com.mmbreakfast.unlod.lod.InvalidLodFileException;
import com.mmbreakfast.unlod.lod.LodFile;
import com.mmbreakfast.unlod.lod.Extractor;
import com.mmbreakfast.unlod.lod.NoSuchEntryException;
import com.mmbreakfast.unlod.lod.PassThroughLodFileExtractor;
import com.mmbreakfast.unlod.lod.RandomAccessFileInputStream;
public class MM6SpritesLodEntry extends LodEntry implements TGADataProducer, SpriteTGADataProducer
{
protected Extractor thePassThroughLodFileExtractor = new PassThroughLodFileExtractor();
protected Extractor theLodFileExtractor = new Extractor();
// entry header
protected static final int ENTRY_NAME__ENTRY_HEADER_OFFSET = 0x00;
protected static final int ENTRY_NAME__ENTRY_HEADER_MAX_LENGTH = 0x0a;
protected static final int DATA_SEGMENT_OFFSET__ENTRY_HEADER_OFFSET = 0x10; // add file header length to get full offset
protected static final int DATA_SEGMENT_LENGTH__ENTRY_HEADER_OFFSET = 0x14; // data_header + data_context length
protected static final int DATA_HEADER_ATTRIBUTE__H_FLIP = 0x0001; // Flip sprite horizontally
protected static final int DATA_HEADER_ATTRIBUTE__V_FLIP = 0x0004; // Flip sprite vertically
protected static final int DATA_HEADER_ATTRIBUTE__Sp_NoClip = 0x0008; // Don't clip sprite to view window
protected static final int DATA_HEADER_ATTRIBUTE__Mask_Glass = 0x0010; // use darkness mask
protected static final int DATA_HEADER_ATTRIBUTE__Mask_Frost = 0x0020; // use mist mask
protected static final int DATA_HEADER_ATTRIBUTE__Mask_Blue = 0x0040; // use blue mask
protected static final int DATA_HEADER_ATTRIBUTE__Mask_Green = 0x0080; // use green mask
protected static final int DATA_HEADER_ATTRIBUTE__Sp_LodMem = 0x0400; // don't attempt to delete map and palette pointers
protected static final int DATA_HEADER_ATTRIBUTE__Sp_Center = 0x0800; // center sprite
// data header
protected static final int DATA_NAME__DATA_HEADER_OFFSET = 0x00;
protected static final int DATA_NAME__DATA_HEADER_MAX_LENGTH = 0x0C;
protected static final int PIXEL_NUMBER__DATA_HEADER_OFFSET = 0x0C; // 4 bytes // number of pixels in sprite
protected static final int TGA_BYTE_WIDTH__DATA_HEADER_OFFSET = 0x10; // 2 bytes
protected static final int TGA_BYTE_HEIGHT__DATA_HEADER_OFFSET = 0x12; // 2 bytes
protected static final int PALETTE_NUMBER__DATA_HEADER_OFFSET = 0x14; // 2 bytes
protected static final int RESOLUTION__DATA_HEADER_OFFSET = 0x16; // 2 bytes // unused?
protected static final int YSKIP__DATA_HEADER_OFFSET = 0x18; // 2 bytes // number of clear lines at bottom
protected static final int ATTRIBUTE_MASK__DATA_HEADER_OFFSET = 0x1a; // 2 bytes
protected static final int DATA_CONTENT_UNCOMPRESSED_SIZE__DATA_HEADER_OFFSET = 0x1c; // 4 bytes
protected static final int DATA_HEADER_LENGTH = 0x20;
private static final int ZERO_COMPRESSION_TABLE__DATA_HEADER_OFFSET = DATA_HEADER_LENGTH; // 8 bytes * width
private static final int ZERO_COMPRESSION_TABLE_DATA_SIZE = 0x08; // per image row
// both START and END are -1 (FF FF FF FF) if all zeros and offset is 00 00 00 00
private static final int ZERO_COMPRESSION_LINE_DATA_START_BYTE_OFFSET = 0x00; // 2 bytes -- number of zero bytes at start of row
private static final int ZERO_COMPRESSION_LINE_DATA_END_BYTE_OFFSET = 0x02; // 2 bytes -- (width - 1) - (number of zero bytes at end of row)
private static final int ZERO_COMPRESSION_LINE_DATA_OFFSET_OFFSET = 0x04; // 2 bytes -- 0-based index into compacted uncompressed data
protected Extractor theFileExtractor = new Extractor();
public MM6SpritesLodEntry(LodFile lodFile, long headerOffset)
throws IOException
{
super(lodFile, headerOffset);
}
public String getTextDescription()
{
return "Name: "
+ getName()
+ "\n"
+ "EntryName: "
+ getEntryName()
+ "\n"
+ "DataName: "
+ getDataName()
+ "\n"
+ "DataType: "
+ getDataType()
+ "\n"
+ "FileType: "
+ getFileType()
+ "\n"
+ "Width: "
+ getByteWidth()
+ "\n"
+ "Height: "
+ getByteHeight()
+ "\n"
+ "PaletteNumber: "
+ getPaletteNumber()
+ "\n"
+ "Data Length: "
+ getDataLength()
+ "\n"
+ "Decompressed Size: "
+ getDecompressedSize()
+ "\n"
+ "Data Header Offset: "
+ getDataHeaderOffset()
+ "\n"
+ "Data Offset: "
+ getDataOffset()
;
}
public int getByteWidth()
{
return ByteConversions.getShortInByteArrayAtPosition(dataHeader, getOffsetInDataHeaderForTGAByteWidth());
}
public int getByteHeight()
{
return ByteConversions.getShortInByteArrayAtPosition(dataHeader, getOffsetInDataHeaderForTGAByteHeight());
}
public long getZeroCompressionTableOffset()
{
return ZERO_COMPRESSION_TABLE__DATA_HEADER_OFFSET;
}
public int[] getPalette() throws IOException
{
BitmapsLodFile cachedBitmapsLodFile = ((MM6SpritesLodFile)getLodFile()).getCachedBitmapsLodFile();
if (null == cachedBitmapsLodFile)
{
File thisFile = this.getLodFile().getFile();
File paletteFile = new File(thisFile.getParent() + File.separator + "BITMAPS.LOD");
RandomAccessFile paletteRandomAccessFile = new RandomAccessFile(paletteFile, "r");
RandomAccessFileInputStream paletteInputStream = new RandomAccessFileInputStream(paletteRandomAccessFile);
BitmapsLodFile paletteLodFile;
try
{
cachedBitmapsLodFile = new BitmapsLodFile(paletteFile, paletteInputStream);
((MM6SpritesLodFile)getLodFile()).setCachedBitmapsLodFile(cachedBitmapsLodFile);
}
catch (InvalidLodFileException exception)
{
IOException ioException = new IOException(exception.getMessage());
ioException.initCause(exception);
throw ioException;
}
}
long paletteNumber = getPaletteNumber();
Map cachedPaletteHashTable = ((MM6SpritesLodFile)getLodFile()).getCachedPaletteHashTable();
int palette[] = (int [])cachedPaletteHashTable.get(new Long(paletteNumber));
if (null != palette) return palette;
String paletteNumberString = String.valueOf(paletteNumber);
while (paletteNumberString.length() < 3) paletteNumberString = "0" + paletteNumberString;
String paletteEntryName = "pal" + paletteNumberString + ".pal";
BitmapsLodEntry paletteLodEntry = null;
try
{
paletteLodEntry = (BitmapsLodEntry)cachedBitmapsLodFile.findLodEntryByFileName(paletteEntryName);
}
catch (NoSuchEntryException exception)
{
IOException ioException = new IOException(exception.getMessage());
ioException.initCause(exception);
throw ioException;
}
int paletteArray[] = new int[768];
long startPosition = paletteLodEntry.getDataOffset();
RandomAccessFile raf =
paletteLodEntry
.getLodFile()
.getRandomAccessFileInputStream()
.getFile();
raf.seek(startPosition);
for (int index = 0; index < 768; ++index)
{
paletteArray[index] = raf.read();
}
cachedPaletteHashTable.put(new Long(paletteNumber), paletteArray);
return paletteArray;
}
protected void computeEntryBasedOffsets()
{
dataHeader = new byte[getDataHeaderLength()];
}
protected void computeDataBasedOffsets()
{
}
protected long getDataHeaderOffset()
{
return getLodFile().getHeaderOffset() + ByteConversions.getIntegerInByteArrayAtPosition(entryHeader, getOffsetInEntryHeaderForDataSegmentOffset());
}
public long getDataOffset()
{
return getDataHeaderOffset() + getDataHeaderLength();
}
protected int getDataLength()
{
return ByteConversions.getIntegerInByteArrayAtPosition(entryHeader, DATA_SEGMENT_LENGTH__ENTRY_HEADER_OFFSET) - getDataHeaderLength();
}
public byte[] getData() throws IOException
{
byte[] rawData = readRawData();
int zeroCompressedTableLength = ZERO_COMPRESSION_TABLE_DATA_SIZE * getByteHeight();
int compactedDataLength = rawData.length - zeroCompressedTableLength;
byte[] compressedImageData = new byte[compactedDataLength];
System.arraycopy(rawData, zeroCompressedTableLength, compressedImageData, 0, compactedDataLength);
byte[] zeroCompressionTableByteArray = new byte[zeroCompressedTableLength];
System.arraycopy(rawData, 0, zeroCompressionTableByteArray, 0, zeroCompressedTableLength);
byte[] compactedData = null;
if (0 == getDecompressedSize())
compactedData = compressedImageData;
else
{
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
theLodFileExtractor.decompress(null, compressedImageData, byteStream, null, true);
compactedData = byteStream.toByteArray();
}
return unZeroCompressDataUsingTable(compactedData, zeroCompressionTableByteArray);
}
public String getResourceType()
{
return getFileType();
}
protected int getOffsetInEntryHeaderForDataSegmentOffset()
{
return DATA_SEGMENT_OFFSET__ENTRY_HEADER_OFFSET;
}
protected int getOffsetInEntryHeaderForDataSegmentLength()
{
return DATA_SEGMENT_LENGTH__ENTRY_HEADER_OFFSET;
}
protected int getDataHeaderLength()
{
return DATA_HEADER_LENGTH;
}
protected int getOffsetInDataHeaderForDataName()
{
return DATA_NAME__DATA_HEADER_OFFSET;
}
protected int getDataNameMaxLength()
{
return DATA_NAME__DATA_HEADER_MAX_LENGTH;
}
protected int getOffsetInDataHeaderForPaletteNumber()
{
return PALETTE_NUMBER__DATA_HEADER_OFFSET;
}
protected long getOffsetInDataHeaderForDataContentUncompressedLength()
{
return DATA_CONTENT_UNCOMPRESSED_SIZE__DATA_HEADER_OFFSET;
}
protected int getOffsetInDataHeaderForPixelNumber()
{
return PIXEL_NUMBER__DATA_HEADER_OFFSET;
}
protected int getOffsetInDataHeaderForResolution()
{
return RESOLUTION__DATA_HEADER_OFFSET;
}
protected int getOffsetInDataHeaderForYSkip()
{
return YSKIP__DATA_HEADER_OFFSET;
}
protected int getOffsetInDataHeaderForAttributeMask()
{
return ATTRIBUTE_MASK__DATA_HEADER_OFFSET;
}
protected int getOffsetInDataHeaderForTGAByteWidth()
{
return TGA_BYTE_WIDTH__DATA_HEADER_OFFSET;
}
protected int getOffsetInDataHeaderForTGAByteHeight()
{
return TGA_BYTE_HEIGHT__DATA_HEADER_OFFSET;
}
public String getFileName()
{
String baseName = getEntryName();
String fileName = null;
if (baseName.toLowerCase().endsWith("." + getFileType().toLowerCase()))
{
fileName = baseName;
}
else
{
fileName = baseName + "." + getFileType();
}
String converterFileType = getFormatConverterFileType();
if (null != converterFileType)
{
if (false == fileName.toLowerCase().endsWith("." + converterFileType.toLowerCase()))
{
fileName = fileName + "." + converterFileType;
}
}
return fileName;
}
protected String getFileType()
{
return "sprite";
}
public String getEntryName()
{
int offset = ENTRY_NAME__ENTRY_HEADER_OFFSET;
int maxLength = ENTRY_NAME__ENTRY_HEADER_MAX_LENGTH;
int length = 0;
while ( (0 != entryHeader[offset + length]) && (length < maxLength) )
length++;
return new String(entryHeader, offset, length);
}
public String getDataName()
{
int offset = getOffsetInDataHeaderForDataName();
int maxLength = getDataNameMaxLength();
int length = 0;
while ( (0 != dataHeader[offset + length]) && (length < maxLength) )
length++;
return new String(dataHeader, offset, length);
}
protected String getDataType()
{
return "sprite";
}
protected long getPaletteNumber()
{
int index = (int)getOffsetInDataHeaderForPaletteNumber();
if (-1 == index) return -1;
return ByteConversions.getIntegerInByteArrayAtPosition(dataHeader, index);
}
public int getPixelNumber()
{
int index = (int)getOffsetInDataHeaderForPixelNumber();
if (-1 == index) return -1;
return ByteConversions.getIntegerInByteArrayAtPosition(dataHeader, index);
}
public int getResolution()
{
int index = (int)getOffsetInDataHeaderForResolution();
if (-1 == index) return -1;
return ByteConversions.getIntegerInByteArrayAtPosition(dataHeader, index);
}
public int getYSkip()
{
int index = (int)getOffsetInDataHeaderForYSkip();
if (-1 == index) return -1;
return ByteConversions.getIntegerInByteArrayAtPosition(dataHeader, index);
}
public int getAttributeMask()
{
int index = (int)getOffsetInDataHeaderForAttributeMask();
if (-1 == index) return -1;
return ByteConversions.getIntegerInByteArrayAtPosition(dataHeader, index);
}
protected long getDecompressedSize()
{
int index = (int)getOffsetInDataHeaderForDataContentUncompressedLength();
if (-1 == index) return -1;
return ByteConversions.getIntegerInByteArrayAtPosition(dataHeader, index);
}
protected Extractor getExtractor()
{
if (0 == getDecompressedSize())
return thePassThroughLodFileExtractor;
else
return theLodFileExtractor;
}
public FormatConverter getFormatConverter()
{
return new TGAToBMPFormatConverter();
}
public String getFormatConverterFileType()
{
return "bmp";
}
protected class NullOutputStream extends OutputStream
{
public void write(int b) throws IOException
{
}
}
protected int getNewWrittenDataContentLength(LodResource lodResourceDataSource)
{
try
{
return (int)writeNewData(lodResourceDataSource, new NullOutputStream(), 0);
}
catch (IOException exception)
{
// Doesn't throw exceptions
exception.printStackTrace();
return -1;
}
}
protected int getUpdatedWrittenDataContentLength(LodResource lodResourceDataSource)
{
try
{
return (int)updateData(lodResourceDataSource, new NullOutputStream(), 0);
}
catch (IOException exception)
{
// Doesn't throw exceptions
exception.printStackTrace();
return -1;
}
}
private long writeEntry(String name, long writtenDataLength, OutputStream outputStream, long entryListOffset, long entryOffset, long dataOffset) throws IOException
{
byte originalEntryHeader[] = getEntryHeader();
byte newEntryHeader[] = new byte[originalEntryHeader.length];
System.arraycopy(originalEntryHeader, 0, newEntryHeader, 0, originalEntryHeader.length);
if (null != name)
{
// update name
Arrays.fill(newEntryHeader, 0, ENTRY_NAME__ENTRY_HEADER_MAX_LENGTH, (byte)0);
byte[] entryNameBytes = name.getBytes();
System.arraycopy(entryNameBytes, 0, newEntryHeader, 0, Math.min(ENTRY_NAME__ENTRY_HEADER_MAX_LENGTH, entryNameBytes.length));
if (entryNameBytes.length < ENTRY_NAME__ENTRY_HEADER_MAX_LENGTH) newEntryHeader[entryNameBytes.length] = 0;
}
// update data offset
ByteConversions.setIntegerInByteArrayAtPosition((dataOffset - entryListOffset), newEntryHeader, getOffsetInEntryHeaderForDataSegmentOffset());
// System.out.println(getName() + "- storing " + String.valueOf(dataOffset - entryListOffset) + " in entry.dataoffset");
// System.out.println(getName() + "- datacontentlength: " + writtenDataLength);
// update data length
ByteConversions.setIntegerInByteArrayAtPosition(writtenDataLength, newEntryHeader, (int)this.getOffsetInEntryHeaderForDataSegmentLength());
// System.out.println(getName() + "- storing " + String.valueOf(writtenDataLength) + " in entry.datalength");
outputStream.write(newEntryHeader);
return dataOffset + writtenDataLength;
}
public long rewriteEntry(OutputStream outputStream, long entryListOffset, long entryOffset, long dataOffset) throws IOException
{
byte[] rawData = readRawData();
return writeEntry(null, rawData.length + getDataHeaderLength(), outputStream, entryListOffset, entryOffset, dataOffset);
}
public long writeNewEntry(LodResource lodResourceDataSource, OutputStream outputStream, long entryListOffset, long entryOffset, long dataOffset) throws IOException
{
return writeEntry(this.getEntryName(), getNewWrittenDataContentLength(lodResourceDataSource), outputStream, entryListOffset, entryOffset, dataOffset);
}
public long updateEntry(LodResource lodResourceDataSource, OutputStream outputStream, long entryListOffset, long entryOffset, long dataOffset) throws IOException
{
return writeEntry(null, getUpdatedWrittenDataContentLength(lodResourceDataSource), outputStream, entryListOffset, entryOffset, dataOffset);
}
protected long writeNewDataHeader(LodResource lodResourceDataSource, OutputStream outputStream, long dataOffset, long uncompressedDataLength, long paletteNumber)
throws IOException
{
byte originalDataHeader[] = this.getDataHeader();
byte newDataHeader[] = new byte[originalDataHeader.length];
System.arraycopy(
originalDataHeader,
0,
newDataHeader,
0,
originalDataHeader.length);
Arrays.fill(newDataHeader, 0, getDataNameMaxLength(), (byte)0);
byte nameBytes[] = this.getDataName().getBytes();
System.arraycopy(nameBytes, 0, newDataHeader, 0, nameBytes.length);
// For uncompressed data, write 0 for uncompressed data length
if (-1 != getOffsetInDataHeaderForDataContentUncompressedLength())
ByteConversions.setIntegerInByteArrayAtPosition(
uncompressedDataLength,
newDataHeader,
(int)this.getOffsetInDataHeaderForDataContentUncompressedLength());
long newDataOffset = dataOffset + newDataHeader.length;
if (lodResourceDataSource instanceof SpriteTGADataProducer)
{
SpriteTGADataProducer tgaDataProducer = (SpriteTGADataProducer)lodResourceDataSource;
int sourcePalette[] = tgaDataProducer.getPalette();
if (null != sourcePalette)
{
byte[] tgaBytes = "TGA".getBytes();
System.arraycopy(tgaBytes, 0, newDataHeader, nameBytes.length + 1, tgaBytes.length);
updateDataHeaderValues(newDataHeader, tgaDataProducer,
paletteNumber);
}
}
outputStream.write(newDataHeader);
return newDataOffset;
}
protected long updateDataHeader(LodResource lodResourceDataSource, OutputStream outputStream, long dataOffset, long uncompressedDataLength, long paletteNumber)
throws IOException
{
byte originalDataHeader[] = this.getDataHeader();
byte newDataHeader[] = new byte[originalDataHeader.length];
System.arraycopy(
originalDataHeader,
0,
newDataHeader,
0,
originalDataHeader.length);
// Arrays.fill(newDataHeader, 0, getDataNameMaxLength(), (byte)0);
// byte nameBytes[] = this.getDataName().getBytes();
// System.arraycopy(nameBytes, 0, newDataHeader, 0, nameBytes.length);
// For uncompressed data, write 0 for uncompressed data length
if (-1 != getOffsetInDataHeaderForDataContentUncompressedLength())
ByteConversions.setIntegerInByteArrayAtPosition(
uncompressedDataLength,
newDataHeader,
(int)this.getOffsetInDataHeaderForDataContentUncompressedLength());
long newDataOffset = dataOffset + newDataHeader.length;
if (lodResourceDataSource instanceof SpriteTGADataProducer)
{
SpriteTGADataProducer tgaDataProducer = (SpriteTGADataProducer)lodResourceDataSource;
int sourcePalette[] = tgaDataProducer.getPalette();
if (null != sourcePalette)
{
// byte[] tgaBytes = "TGA".getBytes();
// System.arraycopy(tgaBytes, 0, newDataHeader, nameBytes.length + 1, tgaBytes.length);
updateDataHeaderValues(newDataHeader, tgaDataProducer,
paletteNumber);
}
}
outputStream.write(newDataHeader);
return newDataOffset;
}
private void updateDataHeaderValues(byte[] newDataHeader,
SpriteTGADataProducer tgaDataProducer, long paletteNumber) {
int sourceWidth = tgaDataProducer.getByteWidth();
int sourceHeight = tgaDataProducer.getByteHeight();
// TODO: implement these methods so sprite headers
int pixelNumber = tgaDataProducer.getPixelNumber();
int resolution = tgaDataProducer.getResolution();
int yskip = tgaDataProducer.getYSkip();
int attributeMask = tgaDataProducer.getAttributeMask();
// I'm computing yskip while setting leftmost and rightmost coordinates of lines.
//
// protected static final int PIXEL_NUMBER__DATA_HEADER_OFFSET = 0x0C; // 4 bytes // number of pixels in sprite
// protected static final int RESOLUTION__DATA_HEADER_OFFSET = 0x16; // 2 bytes // unused?
// private static final int YSKIP__DATA_HEADER_OFFSET = 0x18; // 2 bytes // number of clear lines at bottom
// add pixel number
ByteConversions.setIntegerInByteArrayAtPosition(pixelNumber, newDataHeader, getOffsetInDataHeaderForPixelNumber());
// add width
ByteConversions.setShortInByteArrayAtPosition((short)sourceWidth, newDataHeader, getOffsetInDataHeaderForTGAByteWidth());
// add height
ByteConversions.setShortInByteArrayAtPosition((short)sourceHeight, newDataHeader, getOffsetInDataHeaderForTGAByteHeight());
// add palette number
ByteConversions.setShortInByteArrayAtPosition((short)paletteNumber, newDataHeader, getOffsetInDataHeaderForPaletteNumber());
// add resolution
ByteConversions.setShortInByteArrayAtPosition((short)resolution, newDataHeader, getOffsetInDataHeaderForResolution());
// add yskip
ByteConversions.setShortInByteArrayAtPosition((short)yskip, newDataHeader, getOffsetInDataHeaderForYSkip());
// add attributeMask
ByteConversions.setShortInByteArrayAtPosition((short)attributeMask, newDataHeader, getOffsetInDataHeaderForAttributeMask());
}
public long writeNewData(LodResource lodResourceDataSource, OutputStream outputStream, long dataOffset)
throws IOException
{
TGADataProducer tgaDataProducer = (TGADataProducer)lodResourceDataSource;
byte uncompactedData[] = lodResourceDataSource.getData();
// System.out.println(getName() + "- DS uncompacted data size: " + uncompactedData.length);
byte zeroCompressionTableData[] = new byte[ZERO_COMPRESSION_TABLE_DATA_SIZE * tgaDataProducer.getByteHeight()];
byte uncompressedData[] = zeroCompressDataIntoTable(tgaDataProducer.getByteWidth(), tgaDataProducer.getByteHeight(), uncompactedData, zeroCompressionTableData);
// System.out.println(getName() + "- DS uncompressed data size: " + uncompressedData.length);
// System.out.println(getName() + "- DS zeroCompressionTabledata size: " + zeroCompressionTableData.length);
byte compressedData[] = theLodFileExtractor.compress(uncompressedData);
byte smallestData[] = null;
int uncompressedDataLengthSpecifier;
if (uncompressedData.length > compressedData.length)
{
smallestData = compressedData;
uncompressedDataLengthSpecifier = uncompressedData.length;
}
else
{
smallestData = uncompressedData;
uncompressedDataLengthSpecifier = 0;
}
// System.out.println(getName() + "- rawdataLength: " + smallestData.length);
int sourcePalette[] = tgaDataProducer.getPalette();
if (null == sourcePalette)
throw new RuntimeException("No source palette");
int originalPalette[] = getPalette();
// IMPLEMENT: verify that palette matches known palette number or create new palette resource
// for now, assume palette doesn't change
long paletteNumber = getPaletteNumber();
for (int i = 0 ; i < sourcePalette.length ; i++) {
if (sourcePalette[i] != originalPalette[i])
System.err.println(getName() + ": Warning: palette doesn't match original palette for sprite!");
// IMPLEMENT: throw harder error
}
// System.out.println("palette: " + sourcePalette.length);
long newDataOffset = writeNewDataHeader(lodResourceDataSource, outputStream, dataOffset, uncompressedDataLengthSpecifier, paletteNumber);
outputStream.write(zeroCompressionTableData);
outputStream.write(smallestData);
long newOffset = newDataOffset + zeroCompressionTableData.length + smallestData.length;
// System.out.println(getName() + " - nextoffset: " + newOffset);
return newOffset;
}
public long updateData(LodResource lodResourceDataSource, OutputStream outputStream, long dataOffset)
throws IOException
{
TGADataProducer tgaDataProducer = (TGADataProducer)lodResourceDataSource;
byte uncompactedData[] = lodResourceDataSource.getData();
// System.out.println(getName() + "- DS uncompacted data size: " + uncompactedData.length);
byte zeroCompressionTableData[] = new byte[ZERO_COMPRESSION_TABLE_DATA_SIZE * tgaDataProducer.getByteHeight()];
byte uncompressedData[] = zeroCompressDataIntoTable(tgaDataProducer.getByteWidth(), tgaDataProducer.getByteHeight(), uncompactedData, zeroCompressionTableData);
// System.out.println(getName() + "- DS uncompressed data size: " + uncompressedData.length);
// System.out.println(getName() + "- DS zeroCompressionTabledata size: " + zeroCompressionTableData.length);
byte compressedData[] = theLodFileExtractor.compress(uncompressedData);
byte smallestData[] = null;
int uncompressedDataLengthSpecifier;
if (uncompressedData.length > compressedData.length)
{
smallestData = compressedData;
uncompressedDataLengthSpecifier = uncompressedData.length;
}
else
{
smallestData = uncompressedData;
uncompressedDataLengthSpecifier = 0;
}
// System.out.println(getName() + "- rawdataLength: " + smallestData.length);
int sourcePalette[] = tgaDataProducer.getPalette();
if (null == sourcePalette)
throw new RuntimeException("No source palette");
// IMPLEMENT: verify that palette matches known palette number or create new palette resource
// for now, assume palette doesn't change
long paletteNumber = getPaletteNumber();
int originalPalette[];
try {
originalPalette = getPalette();
} catch (IOException ioException) {
if (ioException.getCause() instanceof NoSuchEntryException)
{
ioException.printStackTrace();
// Meaningless image, but we can continue past it -- generate unique value for every palette point.
originalPalette = new int[768];
for (int i = 0; i < originalPalette.length; ++i)
{
originalPalette[i] = i;
}
}
else
{
throw ioException;
}
}
for (int i = 0 ; i < sourcePalette.length ; i++) {
if (sourcePalette[i] != originalPalette[i])
System.err.println(getName() + ": Warning: palette doesn't match original palette for sprite!");
// IMPLEMENT: throw harder error
}
// System.out.println("palette: " + sourcePalette.length);
long newDataOffset = updateDataHeader(lodResourceDataSource, outputStream, dataOffset, uncompressedDataLengthSpecifier, paletteNumber);
outputStream.write(zeroCompressionTableData);
outputStream.write(smallestData);
long newOffset = newDataOffset + zeroCompressionTableData.length + smallestData.length;
// System.out.println(getName() + " - nextoffset: " + newOffset);
return newOffset;
}
public long rewriteData(OutputStream outputStream, long dataOffset)
throws IOException
{
outputStream.write(getDataHeader());
byte[] rawData = readRawData();
outputStream.write(rawData);
long newOffset = dataOffset + getDataHeaderLength() + rawData.length;
// System.out.println(getName() + " - nextoffset: " + newOffset);
return newOffset;
}
public class ZeroCompressionTableEntry
{
short lineDataStart = -1;
short lineDataEnd = -1;
int lineDataOffset = -1;
public ZeroCompressionTableEntry(short lineDataStart, short lineDataEnd, int lineDataOffset)
{
this.lineDataStart = lineDataStart;
this.lineDataEnd = lineDataEnd;
this.lineDataOffset = lineDataOffset;
}
public String toString()
{
String output = super.toString();
output += ": lineDataStart=" + lineDataStart;
output += ", lineDataEnd=" + lineDataEnd;
output += ", lineDataOffset=" + lineDataOffset;
return output;
}
}
public ZeroCompressionTableEntry[] getZeroCompressionTable(byte zeroCompressionTableData[]) throws IOException
{
int imageHeight = getByteHeight();
ZeroCompressionTableEntry zeroCompressionTable[] = new ZeroCompressionTableEntry[imageHeight];
for (int row = 0; row < imageHeight; ++row)
{
int rowOffset = row * ZERO_COMPRESSION_TABLE_DATA_SIZE;
short lineDataStart = ByteConversions.getShortInByteArrayAtPosition(zeroCompressionTableData, rowOffset + ZERO_COMPRESSION_LINE_DATA_START_BYTE_OFFSET);
short lineDataEnd = ByteConversions.getShortInByteArrayAtPosition(zeroCompressionTableData, rowOffset + ZERO_COMPRESSION_LINE_DATA_END_BYTE_OFFSET);
int lineDataOffset = ByteConversions.getIntegerInByteArrayAtPosition(zeroCompressionTableData, rowOffset + ZERO_COMPRESSION_LINE_DATA_OFFSET_OFFSET);
zeroCompressionTable[row] = new ZeroCompressionTableEntry(lineDataStart, lineDataEnd, lineDataOffset);
}
return zeroCompressionTable;
}
/**
* @param uncompactedData
* @param zeroCompressionTableData
* @return
*/
private byte[] zeroCompressDataIntoTable(int imageWidth, int imageHeight, byte[] uncompactedData, byte[] zeroCompressionTableData)
{
byte pessimisticCompactedData[] = new byte[uncompactedData.length];
int lineDataOffset = 0;
int uncompactedByteArrayIndex = 0;
for (int row = 0; row < imageHeight; ++row)
{
int tableBase = row * ZERO_COMPRESSION_TABLE_DATA_SIZE;
int dataBase = row * imageWidth;
short lineDataStart = 0;
short lineDataEnd = 0;
int col = 0;
while ((col < imageWidth) && (0 == uncompactedData[dataBase + col]))
{
col++;
lineDataStart++;
}
short zerosAtEndOfRow = 0;
col = imageWidth - 1;
while ((col >= 0) && (0 == uncompactedData[dataBase + col]))
{
col--;
zerosAtEndOfRow++;
}
lineDataEnd = (short)((imageWidth - 1) - zerosAtEndOfRow);
if (lineDataStart == imageWidth)
{
// all zeros
// assign to table
ByteConversions.setShortInByteArrayAtPosition((short)-1, zeroCompressionTableData, tableBase + ZERO_COMPRESSION_LINE_DATA_START_BYTE_OFFSET);
ByteConversions.setShortInByteArrayAtPosition((short)-1, zeroCompressionTableData, tableBase + ZERO_COMPRESSION_LINE_DATA_END_BYTE_OFFSET);
ByteConversions.setIntegerInByteArrayAtPosition(0, zeroCompressionTableData, tableBase + ZERO_COMPRESSION_LINE_DATA_OFFSET_OFFSET);
}
else
{
// assign to table
ByteConversions.setShortInByteArrayAtPosition(lineDataStart, zeroCompressionTableData, tableBase + ZERO_COMPRESSION_LINE_DATA_START_BYTE_OFFSET);
ByteConversions.setShortInByteArrayAtPosition(lineDataEnd, zeroCompressionTableData, tableBase + ZERO_COMPRESSION_LINE_DATA_END_BYTE_OFFSET);
ByteConversions.setIntegerInByteArrayAtPosition(lineDataOffset, zeroCompressionTableData, tableBase + ZERO_COMPRESSION_LINE_DATA_OFFSET_OFFSET);
for (col = lineDataStart; col <= lineDataEnd; ++col)
pessimisticCompactedData[uncompactedByteArrayIndex++] = uncompactedData[dataBase + col];
}
lineDataOffset = uncompactedByteArrayIndex;
}
byte compactedData[] = new byte[uncompactedByteArrayIndex];
System.arraycopy(pessimisticCompactedData, 0, compactedData, 0, uncompactedByteArrayIndex);
return compactedData;
}
public byte[] unZeroCompressDataUsingTable(byte[] compactedByteArray, byte[] zeroCompressionTableByteArray) throws IOException
{
byte uncompactedByteArray[] = new byte[(getByteWidth() * getByteHeight())];
ZeroCompressionTableEntry zeroCompressionTable[] = getZeroCompressionTable(zeroCompressionTableByteArray);
int uncompactedByteArrayIndex = 0;
for (int index = 0; index < getByteHeight(); ++index)
{
short lineDataStart = zeroCompressionTable[index].lineDataStart;
short lineDataEnd = zeroCompressionTable[index].lineDataEnd;
int lineDataOffset = zeroCompressionTable[index].lineDataOffset;
if ( (-1 != lineDataStart) && (-1 != lineDataEnd) )
{
for (int zeroIndex = 0; zeroIndex < lineDataStart; ++zeroIndex)
uncompactedByteArray[uncompactedByteArrayIndex++] = 0;
int compactedIndex = lineDataOffset;
for (int lineDataIndex = lineDataStart; lineDataIndex <= lineDataEnd; ++lineDataIndex)
uncompactedByteArray[uncompactedByteArrayIndex++] = compactedByteArray[compactedIndex++];
}
for (int zeroIndex = (lineDataEnd + 1); zeroIndex < getByteWidth(); ++zeroIndex)
uncompactedByteArray[uncompactedByteArrayIndex++] = 0;
}
return uncompactedByteArray;
}
}