mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 06:53:43 +00:00
1320 lines
32 KiB
C++
1320 lines
32 KiB
C++
#include "MemReaderStream.h"
|
|
#include "MMHandleBlock.h"
|
|
#include "ResourceCompiledTypeList.h"
|
|
#include "ResourceFile.h"
|
|
#include "ResourceManager.h"
|
|
#include "CFileStream.h"
|
|
#include "QDPictDecoder.h"
|
|
#include "QDPictEmitContext.h"
|
|
#include "QDPictEmitScanlineParameters.h"
|
|
#include "QDPictHeader.h"
|
|
#include "QDPictOpcodes.h"
|
|
#include "RGBAColor.h"
|
|
#include "SharedTypes.h"
|
|
#include "stb_image_write.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
|
|
using namespace PortabilityLayer;
|
|
|
|
struct TempImage
|
|
{
|
|
size_t m_width;
|
|
size_t m_height;
|
|
|
|
Rect m_rect;
|
|
|
|
RGBAColor *m_contents;
|
|
};
|
|
|
|
void UnpackBits8(std::vector<uint8_t> &outVector, const std::vector<uint8_t> &inData)
|
|
{
|
|
size_t count = inData.size();
|
|
size_t offset = 0;
|
|
while (offset < count)
|
|
{
|
|
int8_t headerByte;
|
|
memcpy(&headerByte, &inData[offset++], 1);
|
|
|
|
if (headerByte >= 0)
|
|
{
|
|
const size_t litCount = headerByte + 1;
|
|
for (size_t i = 0; i < litCount; i++)
|
|
outVector.push_back(inData[offset++]);
|
|
}
|
|
else if (headerByte < 0 && headerByte != -128)
|
|
{
|
|
const size_t repCount = (1 - headerByte);
|
|
const uint8_t repeated = inData[offset++];
|
|
for (size_t i = 0; i < repCount; i++)
|
|
outVector.push_back(repeated);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UnpackBits16(std::vector<uint16_t> &outVector, const std::vector<uint8_t> &inData)
|
|
{
|
|
size_t count = inData.size();
|
|
size_t offset = 0;
|
|
while (offset < count)
|
|
{
|
|
int8_t headerByte;
|
|
memcpy(&headerByte, &inData[offset++], 1);
|
|
|
|
if (headerByte >= 0)
|
|
{
|
|
const size_t litCount = headerByte + 1;
|
|
for (size_t i = 0; i < litCount; i++)
|
|
{
|
|
const uint8_t highByte = inData[offset++];
|
|
const uint8_t lowByte = inData[offset++];
|
|
const uint16_t combined = (highByte << 8) | lowByte;
|
|
outVector.push_back(combined);
|
|
}
|
|
}
|
|
else if (headerByte < 0 && headerByte != -128)
|
|
{
|
|
const size_t repCount = (1 - headerByte);
|
|
|
|
const uint8_t highByte = inData[offset++];
|
|
const uint8_t lowByte = inData[offset++];
|
|
const uint16_t combined = (highByte << 8) | lowByte;
|
|
|
|
for (size_t i = 0; i < repCount; i++)
|
|
outVector.push_back(combined);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlitLineRGB8(const std::vector<uint8_t> &data, size_t first, RGBAColor *outScanline, size_t actualSize)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
{
|
|
RGBAColor &color = outScanline[i];
|
|
color.r = data[first + 0 * actualSize + i];
|
|
color.g = data[first + 1 * actualSize + i];
|
|
color.b = data[first + 2 * actualSize + i];
|
|
color.a = 255;
|
|
}
|
|
}
|
|
|
|
uint8_t FiveToEight(uint8_t v)
|
|
{
|
|
return static_cast<uint8_t>((v << 3) | (v >> 2));
|
|
}
|
|
|
|
void BlitLine16(const std::vector<uint16_t> &data, size_t first, RGBAColor *outScanline, size_t actualSize)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
{
|
|
RGBAColor &color = outScanline[i];
|
|
const uint16_t packedInput = data[first + i];
|
|
color.b = FiveToEight(packedInput & 0x1f);
|
|
color.g = FiveToEight((packedInput >> 5) & 0x1f);
|
|
color.r = FiveToEight((packedInput >> 10) & 0x1f);
|
|
|
|
color.a = 255;
|
|
}
|
|
}
|
|
|
|
void BlitLine1(const std::vector<uint8_t> &data, size_t first, RGBAColor *outScanline, size_t actualSize)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
{
|
|
RGBAColor &color = outScanline[i];
|
|
|
|
if ((data[first + i / 8] & (128 >> (i & 0x7))) == 0)
|
|
color.r = color.g = color.b = 255;
|
|
else
|
|
color.r = color.g = color.b = 0;
|
|
color.a = 255;
|
|
}
|
|
}
|
|
|
|
void BlitLineIndexed(const std::vector<uint8_t> &data, size_t first, RGBAColor *outScanline, size_t actualSize, int componentSize, const RGBAColor *clut)
|
|
{
|
|
if (componentSize == 1)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
{
|
|
const int bitShift = 7 - (i & 7);
|
|
const int colorIndex = (data[first + i / 8] >> bitShift) & 0x1;
|
|
outScanline[i] = clut[colorIndex];
|
|
}
|
|
}
|
|
else if (componentSize == 2)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
{
|
|
const int bitShift = 6 - (2 * (i & 3));
|
|
const int colorIndex = (data[first + i / 4] >> bitShift) & 0x3;
|
|
outScanline[i] = clut[colorIndex];
|
|
}
|
|
}
|
|
else if (componentSize == 4)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
{
|
|
const int bitShift = 4 - (4 * (i & 1));
|
|
const int colorIndex = (data[first + i / 2] >> bitShift) & 0xf;
|
|
outScanline[i] = clut[colorIndex];
|
|
}
|
|
}
|
|
else if (componentSize == 8)
|
|
{
|
|
for (size_t i = 0; i < actualSize; i++)
|
|
outScanline[i] = clut[data[first + i]];
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
void DecodeClutItemChannel(uint8_t &outChannel, const uint8_t *inChannel)
|
|
{
|
|
outChannel = inChannel[0];
|
|
}
|
|
|
|
void DecodeClutItem(RGBAColor &decoded, const BEColorTableItem &clutItem)
|
|
{
|
|
DecodeClutItemChannel(decoded.r, clutItem.m_red);
|
|
DecodeClutItemChannel(decoded.g, clutItem.m_green);
|
|
DecodeClutItemChannel(decoded.b, clutItem.m_blue);
|
|
decoded.a = 255;
|
|
}
|
|
|
|
void AuditPackBitsRectOld(MemReaderStream &stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, TempImage &image)
|
|
{
|
|
uint16_t rowSizeBytes = 0;
|
|
|
|
bool isPixMap = false;
|
|
if (isDirect)
|
|
{
|
|
isPixMap = true;
|
|
// Skip base address (4) + unknown (2)
|
|
stream.SeekCurrent(4);
|
|
|
|
stream.Read(&rowSizeBytes, sizeof(BEUInt16_t));
|
|
ByteSwap::BigUInt16(rowSizeBytes);
|
|
|
|
rowSizeBytes &= 0x7fff;
|
|
}
|
|
else
|
|
{
|
|
stream.Read(&rowSizeBytes, sizeof(BEUInt16_t));
|
|
ByteSwap::BigUInt16(rowSizeBytes);
|
|
|
|
if (pictVersion == 2)
|
|
{
|
|
if ((rowSizeBytes & 0x8000) != 0)
|
|
{
|
|
isPixMap = true;
|
|
rowSizeBytes &= 0x7fff;
|
|
}
|
|
else
|
|
printf("Actually a bitmap\n");
|
|
}
|
|
}
|
|
|
|
if (!isPixMap)
|
|
{
|
|
printf("Bitmap?\n");
|
|
BEBitMap bitMap;
|
|
stream.Read(&bitMap, sizeof(BEBitMap));
|
|
|
|
BERect srcRectBE;
|
|
stream.Read(&srcRectBE, sizeof(BERect));
|
|
|
|
Rect srcRect = srcRectBE.ToRect();
|
|
|
|
BERect destRectBE;
|
|
stream.Read(&destRectBE, sizeof(BERect));
|
|
|
|
Rect destRect = destRectBE.ToRect();
|
|
|
|
BEUInt16_t transferMode;
|
|
stream.Read(&transferMode, 2);
|
|
|
|
if (hasRegion)
|
|
{
|
|
BEUInt16_t regionSize;
|
|
stream.Read(®ionSize, 2);
|
|
|
|
stream.SeekCurrent(regionSize - 2);
|
|
}
|
|
|
|
assert(static_cast<size_t>(destRect.right) <= image.m_width);
|
|
assert(static_cast<size_t>(destRect.left) <= image.m_width);
|
|
assert(destRect.left < destRect.right);
|
|
|
|
const int numCols = destRect.right - destRect.left;
|
|
const int firstCol = destRect.left;
|
|
const int firstRow = destRect.top;
|
|
|
|
const int numLines = bitMap.m_bounds.bottom - bitMap.m_bounds.top;
|
|
if (isPackedFlag && rowSizeBytes >= 8)
|
|
{
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressed;
|
|
compressed.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressed[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
std::vector<uint8_t> decompressed;
|
|
UnpackBits8(decompressed, compressed);
|
|
|
|
BlitLine1(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::vector<uint8_t> decompressed;
|
|
decompressed.resize(rowSizeBytes);
|
|
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
if (stream.Read(&decompressed[0], rowSizeBytes) != rowSizeBytes)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
BlitLine1(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Read RowBytes, if high bit is set, revert to bitmap
|
|
// If rowBytes < 8 or pack type == 1, data is unpacked
|
|
// If pack type == 2, pad byte of 32-bit data is dropped (direct pixels only, 32-bit images)
|
|
// If pack type == 3, 16-bit RLE
|
|
// If pack type == 4, 8-bit planar component RLE
|
|
|
|
GP_STATIC_ASSERT(sizeof(BEPixMap) == 44);
|
|
|
|
BEPixMap pixMap;
|
|
stream.Read(&pixMap, sizeof(BEPixMap));
|
|
|
|
printf("Pack type: %i\n", static_cast<int>(pixMap.m_packType));
|
|
|
|
BEColorTableHeader clutHeader;
|
|
BEColorTableItem clutItems[256];
|
|
|
|
RGBAColor colors[256];
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
RGBAColor &color = colors[i];
|
|
color.r = color.g = color.b = i;
|
|
color.a = 255;
|
|
}
|
|
|
|
if (isDirect)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
stream.Read(&clutHeader, sizeof(BEColorTableHeader));
|
|
|
|
assert(clutHeader.m_numItemsMinusOne <= 255);
|
|
|
|
printf("CLUT items: %i\n", static_cast<int>(clutHeader.m_numItemsMinusOne));
|
|
uint16_t numColors = clutHeader.m_numItemsMinusOne + 1;
|
|
|
|
stream.Read(clutItems, sizeof(BEColorTableItem) * numColors);
|
|
|
|
for (int i = 0; i < numColors; i++)
|
|
DecodeClutItem(colors[i], clutItems[i]);
|
|
}
|
|
|
|
BERect srcRectBE;
|
|
stream.Read(&srcRectBE, sizeof(BERect));
|
|
|
|
BERect destRectBE;
|
|
stream.Read(&destRectBE, sizeof(BERect));
|
|
|
|
const Rect srcRect = srcRectBE.ToRect();
|
|
const Rect destRect = destRectBE.ToRect();
|
|
|
|
assert(static_cast<size_t>(destRect.right) <= image.m_width);
|
|
assert(static_cast<size_t>(destRect.left) <= image.m_width);
|
|
assert(destRect.left < destRect.right);
|
|
|
|
const int numCols = destRect.right - destRect.left;
|
|
const int firstCol = destRect.left;
|
|
const int firstRow = destRect.top;
|
|
|
|
BEUInt16_t transferMode;
|
|
stream.Read(&transferMode, 2);
|
|
|
|
//assert(srcRect.left == pixMap.m_bounds.left && srcRect.top == pixMap.m_bounds.top);
|
|
//assert(srcRect.left == destRect.left && srcRect.top == destRect.top);
|
|
|
|
|
|
assert(isPackedFlag || rowSizeBytes < 8 || pixMap.m_packType == 1);
|
|
|
|
if (hasRegion)
|
|
{
|
|
BEUInt16_t regionSize;
|
|
stream.Read(®ionSize, 2);
|
|
|
|
stream.SeekCurrent(regionSize - 2);
|
|
}
|
|
|
|
int packType = pixMap.m_packType;
|
|
if (isDirect && packType == 0)
|
|
{
|
|
switch (static_cast<int>(pixMap.m_componentSize))
|
|
{
|
|
case 16:
|
|
packType = 3;
|
|
break;
|
|
case 32:
|
|
packType = 4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isDirect)
|
|
{
|
|
if (packType == 4)
|
|
{
|
|
assert(pixMap.m_componentCount == 3);
|
|
assert(pixMap.m_componentSize == 8);
|
|
}
|
|
else if (packType == 3)
|
|
{
|
|
assert(pixMap.m_componentCount == 3);
|
|
assert(pixMap.m_componentSize == 5);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
int numLines = pixMap.m_bounds.bottom - pixMap.m_bounds.top;
|
|
if (isDirect == false && (pixMap.m_packType == 1 || rowSizeBytes < 8))
|
|
{
|
|
std::vector<uint8_t> directLine;
|
|
directLine.resize(rowSizeBytes);
|
|
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
if (stream.Read(&directLine[0], rowSizeBytes) != rowSizeBytes)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if (pixMap.m_componentSize == 1)
|
|
{
|
|
BlitLine1(directLine, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
else if (pixMap.m_componentSize == 8)
|
|
{
|
|
BlitLineIndexed(directLine, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols, pixMap.m_componentSize, colors);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
else if (isDirect && packType == 4)
|
|
{
|
|
assert(pixMap.m_componentCount == 3);
|
|
assert(pixMap.m_componentSize == 8);
|
|
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressedLine;
|
|
compressedLine.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
std::vector<uint8_t> decompressed;
|
|
UnpackBits8(decompressed, compressedLine);
|
|
|
|
BlitLineRGB8(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
}
|
|
else if (isDirect && packType == 3)
|
|
{
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressedLine;
|
|
compressedLine.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
std::vector<uint16_t> decompressed;
|
|
UnpackBits16(decompressed, compressedLine);
|
|
|
|
BlitLine16(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
}
|
|
else if (packType <= 1)
|
|
{
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressedLine;
|
|
compressedLine.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
std::vector<uint8_t> decompressed;
|
|
UnpackBits8(decompressed, compressedLine);
|
|
|
|
BlitLineIndexed(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols, pixMap.m_componentSize, colors);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if ((stream.Tell() & 1) != 0)
|
|
stream.SeekCurrent(1);
|
|
}
|
|
}
|
|
|
|
|
|
void AuditPackBitsRect(MemReaderStream &stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, TempImage &image)
|
|
{
|
|
if (false)
|
|
{
|
|
AuditPackBitsRectOld(stream, pictVersion, isPackedFlag, hasRegion, isDirect, image);
|
|
return;
|
|
}
|
|
|
|
uint16_t rowSizeBytes = 0;
|
|
|
|
bool isPixMap = false;
|
|
if (isDirect)
|
|
{
|
|
isPixMap = true;
|
|
// Skip base address
|
|
stream.SeekCurrent(4);
|
|
|
|
stream.Read(&rowSizeBytes, sizeof(BEUInt16_t));
|
|
ByteSwap::BigUInt16(rowSizeBytes);
|
|
|
|
rowSizeBytes &= 0x7fff;
|
|
}
|
|
else
|
|
{
|
|
stream.Read(&rowSizeBytes, sizeof(BEUInt16_t));
|
|
ByteSwap::BigUInt16(rowSizeBytes);
|
|
|
|
if (pictVersion == 2)
|
|
{
|
|
if ((rowSizeBytes & 0x8000) != 0)
|
|
{
|
|
isPixMap = true;
|
|
rowSizeBytes &= 0x7fff;
|
|
}
|
|
else
|
|
printf("Actually a bitmap\n");
|
|
}
|
|
}
|
|
|
|
Rect srcRect;
|
|
Rect destRect;
|
|
uint16_t transferMode;
|
|
|
|
BEColorTableHeader clutHeader;
|
|
BEColorTableItem clutItems[256];
|
|
|
|
RGBAColor colors[256];
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
RGBAColor &color = colors[i];
|
|
color.r = color.g = color.b = i;
|
|
color.a = 255;
|
|
}
|
|
|
|
BEPixMap pixMapBE;
|
|
|
|
int packType = 0;
|
|
|
|
if (!isPixMap)
|
|
{
|
|
BEBitMap bitMap;
|
|
stream.Read(&bitMap, sizeof(BEBitMap));
|
|
|
|
packType = (isPackedFlag && rowSizeBytes >= 8) ? 0 : 1;
|
|
|
|
pixMapBE.m_bounds = bitMap.m_bounds;
|
|
pixMapBE.m_version = 2;
|
|
pixMapBE.m_packType = packType;
|
|
pixMapBE.m_hRes = 72 << 16;
|
|
pixMapBE.m_vRes = 72 << 16;
|
|
pixMapBE.m_vRes = 72 << 16;
|
|
pixMapBE.m_pixelType = 16; // FIXME: Use direct instead
|
|
pixMapBE.m_pixelSize = 1;
|
|
pixMapBE.m_componentCount = 1;
|
|
pixMapBE.m_componentSize = 1;
|
|
pixMapBE.m_planeSizeBytes = 0;
|
|
pixMapBE.m_clutHandle = 0;
|
|
pixMapBE.m_unused = 0;
|
|
|
|
BERect srcRectBE;
|
|
stream.Read(&srcRectBE, sizeof(BERect));
|
|
|
|
BERect destRectBE;
|
|
stream.Read(&destRectBE, sizeof(BERect));
|
|
|
|
srcRect = srcRectBE.ToRect();
|
|
destRect = destRectBE.ToRect();
|
|
|
|
stream.Read(&transferMode, 2);
|
|
ByteSwap::BigUInt16(transferMode);
|
|
|
|
if (hasRegion)
|
|
{
|
|
BEUInt16_t regionSize;
|
|
stream.Read(®ionSize, 2);
|
|
|
|
stream.SeekCurrent(regionSize - 2);
|
|
}
|
|
|
|
colors[0].r = colors[0].g = colors[0].b = 255;
|
|
colors[0].a = 255;
|
|
|
|
colors[1].r = colors[1].g = colors[1].b = 0;
|
|
colors[1].a = 255;
|
|
}
|
|
else
|
|
{
|
|
// If rowBytes < 8 or pack type == 1, data is unpacked
|
|
// If pack type == 2, pad byte of 32-bit data is dropped (direct pixels only, 32-bit images)
|
|
// If pack type == 3, 16-bit RLE
|
|
// If pack type == 4, 8-bit planar component RLE
|
|
|
|
GP_STATIC_ASSERT(sizeof(BEPixMap) == 44);
|
|
|
|
stream.Read(&pixMapBE, sizeof(BEPixMap));
|
|
|
|
if (isDirect)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
stream.Read(&clutHeader, sizeof(BEColorTableHeader));
|
|
|
|
assert(clutHeader.m_numItemsMinusOne <= 255);
|
|
|
|
printf("CLUT items: %i\n", static_cast<int>(clutHeader.m_numItemsMinusOne));
|
|
uint16_t numColors = clutHeader.m_numItemsMinusOne + 1;
|
|
|
|
stream.Read(clutItems, sizeof(BEColorTableItem) * numColors);
|
|
|
|
for (int i = 0; i < numColors; i++)
|
|
DecodeClutItem(colors[i], clutItems[i]);
|
|
}
|
|
|
|
BERect srcRectBE;
|
|
stream.Read(&srcRectBE, sizeof(BERect));
|
|
|
|
BERect destRectBE;
|
|
stream.Read(&destRectBE, sizeof(BERect));
|
|
|
|
srcRect = srcRectBE.ToRect();
|
|
destRect = destRectBE.ToRect();
|
|
|
|
assert(destRect.left < destRect.right);
|
|
assert(destRect.top < destRect.bottom);
|
|
|
|
const int numCols = destRect.right - destRect.left;
|
|
const int firstCol = destRect.left;
|
|
const int firstRow = destRect.top;
|
|
|
|
BEUInt16_t transferMode;
|
|
stream.Read(&transferMode, 2);
|
|
|
|
//assert(srcRect.left == pixMap.m_bounds.left && srcRect.top == pixMap.m_bounds.top);
|
|
//assert(srcRect.left == destRect.left && srcRect.top == destRect.top);
|
|
|
|
assert(srcRect.left == pixMapBE.m_bounds.left && srcRect.top == pixMapBE.m_bounds.top);
|
|
|
|
assert(isPackedFlag || rowSizeBytes < 8 || pixMapBE.m_packType == 1);
|
|
|
|
if (hasRegion)
|
|
{
|
|
BEUInt16_t regionSize;
|
|
stream.Read(®ionSize, 2);
|
|
|
|
stream.SeekCurrent(regionSize - 2);
|
|
}
|
|
|
|
packType = pixMapBE.m_packType;
|
|
if (isDirect && packType == 0)
|
|
{
|
|
switch (static_cast<int>(pixMapBE.m_componentSize))
|
|
{
|
|
case 16:
|
|
packType = 3;
|
|
break;
|
|
case 32:
|
|
packType = 4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isDirect)
|
|
{
|
|
if (packType == 4)
|
|
{
|
|
assert(pixMapBE.m_componentCount == 3);
|
|
assert(pixMapBE.m_componentSize == 8);
|
|
}
|
|
else if (packType == 3)
|
|
{
|
|
assert(pixMapBE.m_componentCount == 3);
|
|
assert(pixMapBE.m_componentSize == 5);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
assert(srcRect.right - srcRect.left == destRect.right - destRect.left);
|
|
assert(srcRect.bottom - srcRect.top == destRect.bottom - destRect.top);
|
|
|
|
assert(srcRect.right - srcRect.left <= pixMapBE.m_bounds.right - pixMapBE.m_bounds.left);
|
|
assert(srcRect.bottom - srcRect.top <= pixMapBE.m_bounds.bottom - pixMapBE.m_bounds.top);
|
|
|
|
bool noBlit = false;
|
|
assert(destRect.left <= destRect.right);
|
|
assert(destRect.top <= destRect.bottom);
|
|
if (destRect.right <= image.m_rect.left)
|
|
noBlit = true;
|
|
if (destRect.left >= image.m_rect.right)
|
|
noBlit = true;
|
|
|
|
if (destRect.bottom <= image.m_rect.top)
|
|
noBlit = true;
|
|
if (destRect.top >= image.m_rect.bottom)
|
|
noBlit = true;
|
|
|
|
if (rowSizeBytes < 8)
|
|
packType = 1;
|
|
|
|
const Rect imageRect = image.m_rect;
|
|
|
|
const int componentSize = pixMapBE.m_componentSize;
|
|
|
|
const int numScanLines = pixMapBE.m_bounds.bottom - pixMapBE.m_bounds.top;
|
|
const int scanLineWidth = pixMapBE.m_bounds.right - pixMapBE.m_bounds.left;
|
|
const int scanLineLeft = destRect.left;
|
|
const int scanLineTop = destRect.top;
|
|
|
|
const int scanLineUsableWidth = std::min(scanLineWidth, static_cast<int>(destRect.right - destRect.left));
|
|
|
|
if (!isDirect)
|
|
{
|
|
assert(packType == 0 || packType == 1);
|
|
}
|
|
|
|
std::vector<RGBAColor> scanline;
|
|
scanline.resize(scanLineWidth);
|
|
|
|
std::vector<uint8_t> decompressedScanline;
|
|
if (packType == 1)
|
|
decompressedScanline.resize(rowSizeBytes);
|
|
|
|
for (int lineNum = 0; lineNum < numScanLines; lineNum++)
|
|
{
|
|
if (packType == 0 || packType > 2)
|
|
{
|
|
// 8-bit RLE
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressedLine;
|
|
compressedLine.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if (packType == 3)
|
|
{
|
|
std::vector<uint16_t> decompressed;
|
|
UnpackBits16(decompressed, compressedLine);
|
|
BlitLine16(decompressed, 0, &scanline[0], scanLineWidth);
|
|
}
|
|
else
|
|
{
|
|
std::vector<uint8_t> decompressed;
|
|
UnpackBits8(decompressed, compressedLine);
|
|
|
|
if (packType == 0)
|
|
BlitLineIndexed(decompressed, 0, &scanline[0], scanLineWidth, componentSize, colors);
|
|
else
|
|
{
|
|
assert(packType == 4);
|
|
BlitLineRGB8(decompressed, 0, &scanline[0], scanLineWidth);
|
|
}
|
|
}
|
|
}
|
|
else if (packType == 1)
|
|
{
|
|
if (stream.Read(&decompressedScanline[0], rowSizeBytes) != rowSizeBytes)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if (componentSize == 1)
|
|
{
|
|
BlitLine1(decompressedScanline, 0, &scanline[0], scanLineWidth);
|
|
}
|
|
else if (componentSize == 8)
|
|
{
|
|
BlitLineIndexed(decompressedScanline, 0, &scanline[0], scanLineWidth, componentSize, colors);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if (!noBlit)
|
|
{
|
|
const int yCoord = scanLineTop + lineNum;
|
|
const int localYCoord = yCoord - image.m_rect.top;
|
|
int localXCoordStart = scanLineLeft - image.m_rect.left;
|
|
int localXCoordEnd = scanLineLeft + scanLineUsableWidth;
|
|
|
|
if (localYCoord < 0 || yCoord >= image.m_rect.bottom)
|
|
continue;
|
|
|
|
size_t firstDestElement = localYCoord * image.m_width;
|
|
size_t firstSrcElement = 0;
|
|
size_t lastSrcElement = scanLineUsableWidth;
|
|
|
|
if (localXCoordStart < 0)
|
|
{
|
|
firstSrcElement += static_cast<size_t>(-localXCoordStart);
|
|
localXCoordStart = 0;
|
|
}
|
|
else if (localXCoordStart > 0)
|
|
{
|
|
firstDestElement += localXCoordStart;
|
|
}
|
|
|
|
if (localXCoordEnd > image.m_rect.right)
|
|
{
|
|
const size_t extra = localXCoordEnd - image.m_rect.right;
|
|
lastSrcElement -= extra;
|
|
}
|
|
|
|
const size_t numElements = lastSrcElement - firstSrcElement;
|
|
const size_t capacity = image.m_width * image.m_height;
|
|
|
|
assert(firstDestElement <= capacity);
|
|
assert(capacity - firstDestElement >= numElements);
|
|
|
|
memcpy(image.m_contents + firstDestElement, &scanline[firstSrcElement], sizeof(RGBAColor) * numElements);
|
|
}
|
|
}
|
|
|
|
/*
|
|
BlitLineRGB8(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
}
|
|
else if (isDirect && packType == 3)
|
|
{
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressedLine;
|
|
compressedLine.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
std::vector<uint16_t> decompressed;
|
|
UnpackBits16(decompressed, compressedLine);
|
|
|
|
BlitLine16(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols);
|
|
}
|
|
}
|
|
else if (packType <= 1)
|
|
{
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
uint16_t lineByteCount;
|
|
if (rowSizeBytes > 250)
|
|
{
|
|
stream.Read(&lineByteCount, 2);
|
|
ByteSwap::BigUInt16(lineByteCount);
|
|
}
|
|
else
|
|
{
|
|
uint8_t lineByteCountSmall;
|
|
stream.Read(&lineByteCountSmall, 1);
|
|
lineByteCount = lineByteCountSmall;
|
|
}
|
|
|
|
std::vector<uint8_t> compressedLine;
|
|
compressedLine.resize(lineByteCount);
|
|
|
|
if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
std::vector<uint8_t> decompressed;
|
|
UnpackBits8(decompressed, compressedLine);
|
|
|
|
BlitLineIndexed(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols, pixMap.m_componentSize, colors);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
*/
|
|
|
|
// Either undocumented behavior or non-compliant PICT resources, not sure
|
|
if (isPixMap && (stream.Tell() & 1) != 0)
|
|
stream.SeekCurrent(1);
|
|
}
|
|
|
|
class PictDumpEmitContext final : public QDPictEmitContext
|
|
{
|
|
public:
|
|
bool SpecifyFrame(const Rect &rect) override
|
|
{
|
|
m_rect = rect;
|
|
m_width = rect.right - rect.left;
|
|
m_height = rect.bottom - rect.top;
|
|
m_image.resize(m_width * m_height);
|
|
return true;
|
|
}
|
|
|
|
Rect ConstrainRegion(const Rect &rect) const override
|
|
{
|
|
return rect.Intersect(m_rect);
|
|
}
|
|
|
|
void Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) override
|
|
{
|
|
m_blitType = sourceType;
|
|
m_params = params;
|
|
m_constraintRegionWidth = params.m_constrainedRegionRight - params.m_constrainedRegionLeft;
|
|
m_constraintRegionStartIndex = params.m_constrainedRegionLeft - params.m_scanlineOriginX;
|
|
m_constraintRegionEndIndex = params.m_constrainedRegionRight - params.m_scanlineOriginX;
|
|
|
|
const size_t firstCol = params.m_constrainedRegionLeft - m_rect.left;
|
|
const size_t firstRow = params.m_firstY - m_rect.top;
|
|
|
|
m_outputIndexStart = firstRow * m_width + firstCol;
|
|
}
|
|
|
|
bool EmitQTContent(IOStream *stream, uint32_t dataSize, bool isCompressed) override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void BlitScanlineAndAdvance(const void *data) override
|
|
{
|
|
const int32_t crRight = m_params.m_constrainedRegionRight;
|
|
const int32_t crLeft = m_params.m_constrainedRegionLeft;
|
|
const size_t constraintRegionStartIndex = m_constraintRegionStartIndex;
|
|
const uint8_t *dataBytes = static_cast<const uint8_t*>(data);
|
|
const size_t outputIndexStart = m_outputIndexStart;
|
|
const RGBAColor *palette = m_params.m_colors;
|
|
const size_t planarSeparation = m_params.m_planarSeparation;
|
|
|
|
static const RGBAColor bwColors[] =
|
|
{
|
|
{ 255, 255, 255, 255 },
|
|
{ 0, 0, 0, 255 }
|
|
};
|
|
|
|
switch (m_blitType)
|
|
{
|
|
case QDPictBlitSourceType_Indexed1Bit:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
|
|
const int bitShift = 7 - (itemIndex & 7);
|
|
const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1;
|
|
m_image[i + outputIndexStart] = palette[colorIndex];
|
|
}
|
|
break;
|
|
case QDPictBlitSourceType_Indexed2Bit:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
|
|
const int bitShift = 6 - (2 * (itemIndex & 1));
|
|
const int colorIndex = (dataBytes[itemIndex / 4] >> bitShift) & 0x3;
|
|
m_image[i + outputIndexStart] = palette[colorIndex];
|
|
}
|
|
break;
|
|
case QDPictBlitSourceType_Indexed4Bit:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
|
|
const int bitShift = 4 - (4 * (itemIndex & 1));
|
|
const int colorIndex = (dataBytes[itemIndex / 2] >> bitShift) & 0xf;
|
|
m_image[i + outputIndexStart] = palette[colorIndex];
|
|
}
|
|
break;
|
|
case QDPictBlitSourceType_Indexed8Bit:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
const uint8_t colorIndex = dataBytes[itemIndex];
|
|
m_image[i + outputIndexStart] = palette[colorIndex];
|
|
}
|
|
break;
|
|
case QDPictBlitSourceType_1Bit:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
|
|
const int bitShift = 7 - (itemIndex & 7);
|
|
const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1;
|
|
m_image[i + outputIndexStart] = bwColors[colorIndex];
|
|
}
|
|
break;
|
|
case QDPictBlitSourceType_RGB15:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
|
|
const uint16_t item = *reinterpret_cast<const uint16_t*>(dataBytes + itemIndex * 2);
|
|
RGBAColor &outputItem = m_image[i + outputIndexStart];
|
|
|
|
outputItem.b = FiveToEight(item & 0x1f);
|
|
outputItem.g = FiveToEight((item >> 5) & 0x1f);
|
|
outputItem.r = FiveToEight((item >> 10) & 0x1f);
|
|
outputItem.a = 255;
|
|
}
|
|
break;
|
|
case QDPictBlitSourceType_RGB24_Multiplane:
|
|
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
|
{
|
|
const size_t itemIndex = i + constraintRegionStartIndex;
|
|
|
|
RGBAColor &outputItem = m_image[i + outputIndexStart];
|
|
|
|
outputItem.r = dataBytes[itemIndex];
|
|
outputItem.g = dataBytes[itemIndex + planarSeparation];
|
|
outputItem.b = dataBytes[itemIndex + planarSeparation * 2];
|
|
outputItem.a = 255;
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
m_outputIndexStart += m_width;
|
|
}
|
|
|
|
bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) override
|
|
{
|
|
m_buffer1.resize(buffer1Size);
|
|
m_buffer2.resize(buffer2Size);
|
|
|
|
buffer1 = &m_buffer1[0];
|
|
buffer2 = &m_buffer2[0];
|
|
|
|
return true;
|
|
}
|
|
|
|
void DumpImageToFile(const char *path)
|
|
{
|
|
stbi_write_png(path, static_cast<int>(m_width), static_cast<int>(m_height), 4, &m_image[0], static_cast<int>(m_width * 4));
|
|
}
|
|
|
|
private:
|
|
Rect m_rect;
|
|
int32_t m_originX;
|
|
int32_t m_originY;
|
|
|
|
size_t m_width;
|
|
size_t m_height;
|
|
size_t m_constraintRegionWidth;
|
|
size_t m_constraintRegionStartIndex;
|
|
size_t m_constraintRegionEndIndex;
|
|
size_t m_outputIndexStart;
|
|
QDPictBlitSourceType m_blitType;
|
|
QDPictEmitScanlineParameters m_params;
|
|
|
|
std::vector<uint8_t> m_buffer1;
|
|
std::vector<uint8_t> m_buffer2;
|
|
|
|
std::vector<RGBAColor> m_image;
|
|
};
|
|
|
|
void AuditPictOps2(const uint8_t *data, size_t sz, const char *dumpPath)
|
|
{
|
|
MemReaderStream stream(data, sz);
|
|
|
|
QDPictDecoder decoder;
|
|
PictDumpEmitContext emitContext;
|
|
|
|
decoder.DecodePict(&stream, &emitContext);
|
|
|
|
emitContext.DumpImageToFile(dumpPath);
|
|
}
|
|
|
|
void AuditPictOps(const uint8_t *data, size_t sz, const char *dumpPath)
|
|
{
|
|
MemReaderStream stream(data, sz);
|
|
|
|
QDPictHeader header;
|
|
|
|
header.Load(&stream);
|
|
|
|
TempImage image;
|
|
image.m_width = header.GetFrame().right - header.GetFrame().left;
|
|
image.m_height = header.GetFrame().bottom - header.GetFrame().top;
|
|
image.m_rect = header.GetFrame();
|
|
image.m_contents = new RGBAColor[image.m_width * image.m_height];
|
|
|
|
BERect scratchBERect;
|
|
Rect scratchRect;
|
|
BEUInt16_t scratchUInt16;
|
|
BEUInt32_t scratchUInt32;
|
|
|
|
printf("Decode\n");
|
|
|
|
uint8_t scratchBytes[64];
|
|
while (stream.Read(scratchBytes, header.GetVersion()) == header.GetVersion())
|
|
{
|
|
bool finished = false;
|
|
|
|
uint16_t opcode = scratchBytes[0];
|
|
if (header.GetVersion() == 2)
|
|
opcode = (opcode << 8) | scratchBytes[1];
|
|
|
|
printf("Opcode: %x\n", opcode);
|
|
|
|
switch (opcode)
|
|
{
|
|
case QDOpcodes::kNoop:
|
|
break;
|
|
case QDOpcodes::kClipRegion:
|
|
if (stream.Read(scratchBytes, 10) != 10 || scratchBytes[0] != 0 || scratchBytes[1] != 10)
|
|
return; // Unknown format region
|
|
|
|
memcpy(&scratchBERect, scratchBytes + 2, 8);
|
|
scratchRect = scratchBERect.ToRect();
|
|
|
|
break;
|
|
case QDOpcodes::kShortComment:
|
|
stream.Read(scratchBytes, 2);
|
|
break;
|
|
case QDOpcodes::kLongComment:
|
|
stream.Read(scratchBytes, 2);
|
|
stream.Read(&scratchUInt16, 2);
|
|
stream.SeekCurrent(scratchUInt16);
|
|
break;
|
|
case QDOpcodes::kBitsRect:
|
|
AuditPackBitsRect(stream, header.GetVersion(), false, false, false, image);
|
|
break;
|
|
case QDOpcodes::kPackBitsRect:
|
|
AuditPackBitsRect(stream, header.GetVersion(), true, false, false, image);
|
|
break;
|
|
case QDOpcodes::kPackBitsRgn:
|
|
AuditPackBitsRect(stream, header.GetVersion(), true, true, false, image);
|
|
break;
|
|
case QDOpcodes::kDirectBitsRect:
|
|
AuditPackBitsRect(stream, header.GetVersion(), true, false, true, image);
|
|
break;
|
|
case QDOpcodes::kDirectBitsRgn:
|
|
AuditPackBitsRect(stream, header.GetVersion(), true, true, true, image);
|
|
break;
|
|
case QDOpcodes::kDefaultHilite:
|
|
break;
|
|
case QDOpcodes::kOpColor:
|
|
stream.SeekCurrent(6);
|
|
break;
|
|
//case QDOpcodes::kFrameRect:
|
|
// stream.SeekCurrent(8);
|
|
// break;
|
|
case QDOpcodes::kEndOfPicture:
|
|
finished = true;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
if (finished)
|
|
break;
|
|
}
|
|
|
|
stbi_write_png(dumpPath, image.m_width, image.m_height, 4, image.m_contents, image.m_width * 4);
|
|
delete[] image.m_contents;
|
|
}
|
|
|
|
int main(int argc, const char **argv)
|
|
{
|
|
const char *files[] =
|
|
{
|
|
"Teddy World", "CD Demo House", "The Asylum Pro", "ApplicationResources", "California or Bust!", "Sampler", "Art Museum", "Castle o' the Air",
|
|
"Davis Station", "Demo House", "Empty House", "Fun House", "Grand Prix", "ImagineHouse PRO II",
|
|
"In The Mirror", "Land of Illusion", "Leviathan", "Metropolis", "Nemo's Market",
|
|
"Rainbow's End", "Slumberland", "SpacePods", "Titanic"
|
|
};
|
|
|
|
for (const char *filename : files)
|
|
{
|
|
std::string fnameStr = filename;
|
|
std::string filePath = (fnameStr == "ApplicationResources") ? "D:\\Source Code\\GlidePort\\Packaged\\" : "D:\\Source Code\\GlidePort\\Packaged\\Houses\\";
|
|
filePath += filename;
|
|
filePath += ".gpr";
|
|
|
|
FILE *f = fopen(filePath.c_str(), "rb");
|
|
CFileStream stream(f);
|
|
|
|
ResourceFile *resFile = ResourceFile::Create();
|
|
if (!resFile->Load(&stream))
|
|
{
|
|
assert(false);
|
|
}
|
|
stream.Close();
|
|
|
|
const ResourceCompiledTypeList *typeList = resFile->GetResourceTypeList('PICT');
|
|
if (!typeList)
|
|
continue;
|
|
|
|
const size_t numRefs = typeList->m_numRefs;
|
|
for (size_t i = 0; i < numRefs; i++)
|
|
{
|
|
const THandle<void> hBlock = resFile->LoadResource('PICT', typeList->m_firstRef[i].m_resID);
|
|
const void *pictData = *hBlock;
|
|
|
|
std::string dumpPath = "D:\\Source Code\\GlidePort\\DebugData\\PictDump\\";
|
|
|
|
dumpPath += filename;
|
|
|
|
char tempPath[128];
|
|
sprintf(tempPath, "%i", static_cast<int>(typeList->m_firstRef[i].m_resID));
|
|
|
|
dumpPath += " ";
|
|
dumpPath += tempPath;
|
|
dumpPath += ".png";
|
|
|
|
AuditPictOps2(static_cast<const uint8_t*>(pictData), hBlock.MMBlock()->m_size, dumpPath.c_str());
|
|
|
|
hBlock.Dispose();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|