mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-28 19:13:49 +02:00
718 lines
18 KiB
Objective-C
718 lines
18 KiB
Objective-C
/*
|
|
* XADSWFParser.m
|
|
*
|
|
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
#import "XADSWFParser.h"
|
|
#import "XADSWFParser.h"
|
|
#import "XADPNGWriter.h"
|
|
#import "CSMemoryHandle.h"
|
|
#import "CSMultiHandle.h"
|
|
#import "CSZlibHandle.h"
|
|
|
|
@implementation XADSWFParser
|
|
|
|
+(int)requiredHeaderSize { return 6; }
|
|
|
|
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
|
|
{
|
|
const uint8_t *bytes=[data bytes];
|
|
int length=[data length];
|
|
|
|
if(length<4) return NO;
|
|
|
|
//NSLog(@"SWF version %d\n",bytes[3]);
|
|
|
|
if(bytes[3]>=32) return NO; // Assume the SWF format doesn't reach version 32 any time soon.
|
|
if(bytes[0]=='F'&&bytes[1]=='W'&&bytes[2]=='S') return YES;
|
|
if(bytes[0]=='C'&&bytes[1]=='W'&&bytes[2]=='S') return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
-(id)init
|
|
{
|
|
if((self=[super init]))
|
|
{
|
|
parser=nil;
|
|
dataobjects=[NSMutableArray new];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[parser release];
|
|
[dataobjects release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(void)parse
|
|
{
|
|
parser=[[XADSWFTagParser parserWithHandle:[self handle]] retain];
|
|
|
|
[parser parseHeader];
|
|
|
|
CSHandle *fh=[parser handle];
|
|
|
|
int numimages=0;
|
|
int numsounds=0;
|
|
int numstreams=0;
|
|
int laststreamframe=-1;
|
|
NSData *jpegtables=nil;
|
|
NSMutableData *mainstream=nil;
|
|
|
|
int tag;
|
|
while((tag=[parser nextTag]) && [self shouldKeepParsing])
|
|
switch(tag)
|
|
{
|
|
case SWFJPEGTables:
|
|
{
|
|
int length=[parser tagLength];
|
|
if(length>=2)
|
|
{
|
|
jpegtables=[fh readDataOfLength:length-2];
|
|
}
|
|
else
|
|
{
|
|
// Apparently sometimes Flash uses a zero-length table tag,
|
|
// and this implicitly means that the header should be a
|
|
// single SOI marker.
|
|
jpegtables=[NSData dataWithBytes:(uint8_t[2]){0xff,0xd8} length:2];
|
|
}
|
|
[dataobjects addObject:jpegtables];
|
|
}
|
|
break;
|
|
|
|
case SWFDefineBitsJPEGTag:
|
|
{
|
|
numimages++;
|
|
|
|
[fh skipBytes:4];
|
|
off_t offset=[fh offsetInFile];
|
|
off_t length=[parser tagBytesLeft];
|
|
|
|
if(!jpegtables) [XADException raiseIllegalDataException];
|
|
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Image %d at frame %d.jpg",numimages,[parser frame]]
|
|
data:jpegtables offset:offset length:length];
|
|
}
|
|
break;
|
|
|
|
case SWFDefineBitsJPEG2Tag:
|
|
case SWFDefineBitsJPEG3Tag:
|
|
case SWFDefineBitsJPEG4Tag:
|
|
{
|
|
numimages++;
|
|
|
|
[fh skipBytes:2];
|
|
|
|
int alphaoffs=0;
|
|
if(tag==SWFDefineBitsJPEG3Tag||tag==SWFDefineBitsJPEG4Tag)
|
|
{
|
|
alphaoffs=[fh readUInt32LE];
|
|
}
|
|
|
|
if(tag==SWFDefineBitsJPEG4Tag)
|
|
{
|
|
[fh skipBytes:2]; // Skip deblocking filter params.
|
|
}
|
|
|
|
off_t startoffs=[fh offsetInFile];
|
|
|
|
int first=[fh readUInt16BE];
|
|
if(first==0x8950)
|
|
{
|
|
// PNG image.
|
|
[self reportInterestingFileWithReason:@"Image with PNG data"];
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Image %d at frame %d.png",numimages,[parser frame]]
|
|
data:[NSData dataWithBytes:(uint8_t[2]){ 0x89,0x50 } length:2]
|
|
offset:[fh offsetInFile] length:[parser tagBytesLeft]];
|
|
}
|
|
else if(first==0x4749)
|
|
{
|
|
// GIF image.
|
|
[self reportInterestingFileWithReason:@"Image with GIF data"];
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Image %d at frame %d.gif",numimages,[parser frame]]
|
|
data:[NSData dataWithBytes:(uint8_t[2]){ 0x47,0x49 } length:2]
|
|
offset:[fh offsetInFile] length:[parser tagBytesLeft]];
|
|
}
|
|
else if(first==0xffd8||first==0xffd9)
|
|
{
|
|
// JPEG image.
|
|
|
|
// Check if there is an invalid EOI/SOI marker pair at
|
|
// the start of the image, and skip it.
|
|
if(first==0xffd9)
|
|
{
|
|
[fh skipBytes:4];
|
|
first=0xffd8;
|
|
}
|
|
|
|
// Parse JPEG markers to find image dimensions, and
|
|
// to remove possible later EOI/SOI pairs.
|
|
int width=0,height=0;
|
|
|
|
CSMemoryHandle *header=[CSMemoryHandle memoryHandleForWriting];
|
|
[header writeUInt16BE:first];
|
|
for(;;)
|
|
{
|
|
int marker=[fh readUInt16BE];
|
|
|
|
if(marker==0xffda)
|
|
{
|
|
[header writeUInt16BE:marker];
|
|
|
|
off_t imagestart=[fh offsetInFile];
|
|
int imagelength=[parser tagBytesLeft];
|
|
|
|
// Calculate alpha channel extent and adjust
|
|
// main image if needed.
|
|
off_t alphastart=0;
|
|
int alphalength=0;
|
|
if(alphaoffs)
|
|
{
|
|
alphastart=startoffs+alphaoffs;
|
|
alphalength=(int)(imagestart+imagelength-alphastart);
|
|
imagelength=(int)(alphastart-imagestart);
|
|
}
|
|
|
|
// Emit main image.
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Image %d at frame %d.jpg",numimages,[parser frame]]
|
|
data:[header data] offset:imagestart length:imagelength];
|
|
|
|
// Emit alpha channel image if one exists.
|
|
if(alphaoffs && width && height)
|
|
{
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Image %d alpha channel at frame %d.png",numimages,[parser frame]]
|
|
losslessFormat:-1 width:width height:height alpha:NO
|
|
offset:alphastart length:alphalength];
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if(marker==0xffd9)
|
|
{
|
|
// EOI marker. This is garbage introduced by Flash.
|
|
// An SOI marker follows. Skip this.
|
|
[fh skipBytes:2];
|
|
}
|
|
else
|
|
{
|
|
// Other markers. Copy, and parse if known.
|
|
int len=[fh readUInt16BE];
|
|
[header writeUInt16BE:marker];
|
|
[header writeUInt16BE:len];
|
|
if(len>2)
|
|
{
|
|
uint8_t contents[len-2];
|
|
[fh readBytes:len-2 toBuffer:contents];
|
|
[header writeBytes:len-2 fromBuffer:contents];
|
|
|
|
if(marker>=0xffc0&&marker<=0xffc3)
|
|
{
|
|
// SOFx marker, extract width and height.
|
|
width=CSUInt16BE(&contents[3]);
|
|
height=CSUInt16BE(&contents[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else NSLog(@"Error loading SWF file: invalid JPEG data in tag %d",[parser tag]);
|
|
}
|
|
break;
|
|
|
|
case SWFDefineBitsLosslessTag:
|
|
case SWFDefineBitsLossless2Tag:
|
|
{
|
|
numimages++;
|
|
|
|
[fh skipBytes:2];
|
|
int format=[fh readUInt8];
|
|
int width=[fh readUInt16LE];
|
|
int height=[fh readUInt16LE];
|
|
|
|
if(format==3||format==4||format==5)
|
|
{
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Image %d at frame %d.png",numimages,[parser frame]]
|
|
losslessFormat:format width:width height:height alpha:tag==SWFDefineBitsLossless2Tag
|
|
offset:[fh offsetInFile] length:[parser tagBytesLeft]];
|
|
}
|
|
else [self reportInterestingFileWithReason:@"Unsupported lossless format %d",format];
|
|
}
|
|
break;
|
|
|
|
case SWFDefineSoundTag:
|
|
{
|
|
numsounds++;
|
|
|
|
[fh skipBytes:2];
|
|
|
|
int flags=[fh readUInt8];
|
|
int format=flags>>4;
|
|
if(format==2)
|
|
{
|
|
// MP3 audio.
|
|
[fh skipBytes:4]; //uint32_t numsamples=[fh readUInt32LE];
|
|
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Sound %d at frame %d.mp3",numsounds,[parser frame]]
|
|
offset:[fh offsetInFile] length:[parser tagBytesLeft]];
|
|
}
|
|
else if(format==0||format==3)
|
|
{
|
|
// Uncompresed audio. Assumes format 0 is little-endian.
|
|
// Add a WAV header.
|
|
|
|
[fh skipBytes:4]; //uint32_t numsamples=[fh readUInt32LE];
|
|
|
|
int length=[parser tagBytesLeft];
|
|
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Sound %d at frame %d.wav",numsounds,[parser frame]]
|
|
data:[self createWAVHeaderForFlags:flags length:length]
|
|
offset:[fh offsetInFile] length:length];
|
|
}
|
|
else if(format==1) // ADPCM
|
|
{
|
|
// Not implemented yet.
|
|
}
|
|
else if(format==6) // Speex?
|
|
{
|
|
// Not implemented yet.
|
|
}
|
|
else [self reportInterestingFileWithReason:@"Unsupported sound format %d",format];
|
|
}
|
|
break;
|
|
|
|
case SWFSoundStreamHeadTag:
|
|
case SWFSoundStreamHead2Tag:
|
|
{
|
|
int flags=[fh readUInt8];
|
|
int format=(flags>>4)&0x0f;
|
|
if(format==2||format==0) // MP3 format - why is 0 MP3? Who knows!
|
|
{
|
|
mainstream=[NSMutableData data];
|
|
[dataobjects addObject:mainstream];
|
|
}
|
|
else if(format==4) // Nellymoser
|
|
{
|
|
// Not implemented yet.
|
|
}
|
|
else [self reportInterestingFileWithReason:@"Unsupported stream format %d",format];
|
|
}
|
|
break;
|
|
|
|
case SWFSoundStreamBlockTag:
|
|
{
|
|
if(laststreamframe!=[parser frame])
|
|
{
|
|
laststreamframe=[parser frame];
|
|
[fh skipBytes:4];
|
|
}
|
|
[mainstream appendData:[fh readDataOfLength:[parser tagBytesLeft]]];
|
|
}
|
|
break;
|
|
|
|
case SWFDefineSpriteTag:
|
|
{
|
|
[parser parseDefineSpriteTag];
|
|
|
|
NSMutableData *substream=nil;
|
|
int lastsubframe=-1;
|
|
|
|
int tag;
|
|
while((tag=[parser nextSubTag]) && [self shouldKeepParsing])
|
|
switch(tag)
|
|
{
|
|
case SWFSoundStreamHeadTag:
|
|
case SWFSoundStreamHead2Tag:
|
|
{
|
|
int flags=[fh readUInt8];
|
|
int format=(flags>>4)&0x0f;
|
|
if(format==2||format==0) // MP3 format - why is 0 MP3? Who knows!
|
|
{
|
|
substream=[NSMutableData data];
|
|
[dataobjects addObject:substream];
|
|
}
|
|
else if(format==4) // Nellymoser
|
|
{
|
|
// Not implemented yet.
|
|
}
|
|
else [self reportInterestingFileWithReason:@"Unsupported stream format %d",format];
|
|
}
|
|
break;
|
|
|
|
case SWFSoundStreamBlockTag:
|
|
{
|
|
if(lastsubframe!=[parser subFrame])
|
|
{
|
|
lastsubframe=[parser subFrame];
|
|
[fh skipBytes:4];
|
|
}
|
|
[substream appendData:[fh readDataOfLength:[parser subTagBytesLeft]]];
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(substream && [substream length])
|
|
{
|
|
numstreams++;
|
|
|
|
[self addEntryWithName:[NSString stringWithFormat:
|
|
@"Stream %d at frame %d.mp3",numstreams,[parser frame]]
|
|
data:substream];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(mainstream && [mainstream length])
|
|
{
|
|
[self addEntryWithName:[NSString stringWithFormat:@"Stream.mp3"]
|
|
data:mainstream];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-(NSData *)createWAVHeaderForFlags:(int)flags length:(int)length
|
|
{
|
|
int samplerate,bitsperchannel,numchannels;
|
|
|
|
switch((flags>>2)&0x03)
|
|
{
|
|
default:
|
|
case 0: samplerate=5512; break; // 5.5125 kHz - what.
|
|
case 1: samplerate=11025; break;
|
|
case 2: samplerate=22050; break;
|
|
case 3: samplerate=44100; break;
|
|
}
|
|
|
|
if(flags&0x02) bitsperchannel=16;
|
|
else bitsperchannel=8;
|
|
|
|
if(flags&0x01) numchannels=2;
|
|
else numchannels=1;
|
|
|
|
uint8_t header[44]=
|
|
{
|
|
'R','I','F','F',0x00,0x00,0x00,0x00,
|
|
'W','A','V','E','f','m','t',' ',0x10,0x00,0x00,0x00,
|
|
0x01,0x00, 0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00, 0x00,0x00, 0x00,0x00,
|
|
'd','a','t','a',0x00,0x00,0x00,0x00,
|
|
};
|
|
|
|
CSSetUInt32LE(&header[4],36+length);
|
|
CSSetUInt16LE(&header[22],numchannels);
|
|
CSSetUInt32LE(&header[24],samplerate);
|
|
CSSetUInt32LE(&header[28],samplerate*numchannels*bitsperchannel/8);
|
|
CSSetUInt16LE(&header[32],numchannels*bitsperchannel/8);
|
|
CSSetUInt16LE(&header[34],bitsperchannel);
|
|
CSSetUInt32LE(&header[40],length);
|
|
|
|
return [NSData dataWithBytes:header length:sizeof(header)];
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void)addEntryWithName:(NSString *)name data:(NSData *)data
|
|
{
|
|
NSUInteger index=[dataobjects indexOfObjectIdenticalTo:data];
|
|
if(index==NSNotFound)
|
|
{
|
|
index=[dataobjects count];
|
|
[dataobjects addObject:data];
|
|
}
|
|
|
|
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
[self XADPathWithString:name],XADFileNameKey,
|
|
[NSNumber numberWithLongLong:[data length]],XADFileSizeKey,
|
|
[self XADStringWithString:[parser isCompressed]?@"Zlib":@"None"],XADCompressionNameKey,
|
|
[NSNumber numberWithInt:index],@"SWFDataIndex",
|
|
nil];
|
|
[self addEntryWithDictionary:dict];
|
|
}
|
|
|
|
-(void)addEntryWithName:(NSString *)name
|
|
offset:(off_t)offset length:(off_t)length
|
|
{
|
|
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
[self XADPathWithString:name],XADFileNameKey,
|
|
[NSNumber numberWithLongLong:length],XADFileSizeKey,
|
|
[NSNumber numberWithLongLong:length],@"SWFDataLength",
|
|
[NSNumber numberWithLongLong:offset],@"SWFDataOffset",
|
|
[self XADStringWithString:[parser isCompressed]?@"Zlib":@"None"],XADCompressionNameKey,
|
|
nil];
|
|
[self addEntryWithDictionary:dict];
|
|
}
|
|
|
|
-(void)addEntryWithName:(NSString *)name data:(NSData *)data
|
|
offset:(off_t)offset length:(off_t)length
|
|
{
|
|
NSUInteger index=[dataobjects indexOfObjectIdenticalTo:data];
|
|
if(index==NSNotFound)
|
|
{
|
|
index=[dataobjects count];
|
|
[dataobjects addObject:data];
|
|
}
|
|
|
|
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
[self XADPathWithString:name],XADFileNameKey,
|
|
[NSNumber numberWithLongLong:length+[data length]],XADFileSizeKey,
|
|
[NSNumber numberWithLongLong:length],@"SWFDataLength",
|
|
[NSNumber numberWithLongLong:offset],@"SWFDataOffset",
|
|
[self XADStringWithString:[parser isCompressed]?@"Zlib":@"None"],XADCompressionNameKey,
|
|
[NSNumber numberWithInt:index],@"SWFDataIndex",
|
|
nil];
|
|
[self addEntryWithDictionary:dict];
|
|
}
|
|
|
|
-(void)addEntryWithName:(NSString *)name losslessFormat:(int)format
|
|
width:(int)width height:(int)height alpha:(BOOL)alpha
|
|
offset:(off_t)offset length:(off_t)length
|
|
{
|
|
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
[self XADPathWithString:name],XADFileNameKey,
|
|
//[NSNumber numberWithLongLong:length],XADFileSizeKey,
|
|
[NSNumber numberWithLongLong:length],@"SWFDataLength",
|
|
[NSNumber numberWithLongLong:offset],@"SWFDataOffset",
|
|
[NSNumber numberWithInt:format],@"SWFLosslessFormat",
|
|
[NSNumber numberWithInt:width],@"SWFLosslessWidth",
|
|
[NSNumber numberWithInt:height],@"SWFLosslessHeight",
|
|
[NSNumber numberWithBool:alpha],@"SWFLosslessAlpha",
|
|
[self XADStringWithString:@"Zlib"],XADCompressionNameKey,
|
|
nil];
|
|
[self addEntryWithDictionary:dict];
|
|
}
|
|
|
|
|
|
|
|
|
|
-(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
|
|
{
|
|
CSHandle *handle=nil;
|
|
NSNumber *offsetnum=[dict objectForKey:@"SWFDataOffset"];
|
|
NSNumber *lengthnum=[dict objectForKey:@"SWFDataLength"];
|
|
if(offsetnum&&lengthnum)
|
|
{
|
|
handle=[[parser handle] nonCopiedSubHandleFrom:[offsetnum longLongValue]
|
|
length:[lengthnum longLongValue]];
|
|
|
|
NSNumber *formatnum=[dict objectForKey:@"SWFLosslessFormat"];
|
|
if(formatnum)
|
|
{
|
|
return [CSMemoryHandle memoryHandleForReadingData:
|
|
[self convertLosslessFormat:[formatnum intValue]
|
|
width:[[dict objectForKey:@"SWFLosslessWidth"] intValue]
|
|
height:[[dict objectForKey:@"SWFLosslessHeight"] intValue]
|
|
alpha:[[dict objectForKey:@"SWFLosslessAlpha"] boolValue]
|
|
handle:handle]];
|
|
}
|
|
}
|
|
|
|
CSHandle *datahandle=nil;
|
|
NSNumber *indexnum=[dict objectForKey:@"SWFDataIndex"];
|
|
if(indexnum)
|
|
{
|
|
datahandle=[CSMemoryHandle memoryHandleForReadingData:
|
|
[dataobjects objectAtIndex:[indexnum intValue]]];
|
|
}
|
|
|
|
if(handle&&datahandle)
|
|
{
|
|
return [CSMultiHandle handleWithHandles:datahandle,handle,nil];
|
|
}
|
|
else if(datahandle)
|
|
{
|
|
return datahandle;
|
|
}
|
|
else
|
|
{
|
|
return handle;
|
|
}
|
|
}
|
|
|
|
-(NSData *)convertLosslessFormat:(int)format width:(int)width height:(int)height
|
|
alpha:(BOOL)alpha handle:(CSHandle *)handle
|
|
{
|
|
XADPNGWriter *png=[XADPNGWriter PNGWriter];
|
|
|
|
switch(format)
|
|
{
|
|
case 3:
|
|
{
|
|
int numcolours=[handle readUInt8]+1;
|
|
|
|
[png addIHDRWithWidth:width height:height bitDepth:8 colourType:3];
|
|
|
|
CSZlibHandle *zh=[CSZlibHandle zlibHandleWithHandle:handle];
|
|
if(alpha)
|
|
{
|
|
uint8_t palette[4*numcolours];
|
|
[zh readBytes:4*numcolours toBuffer:palette];
|
|
|
|
uint8_t plte[3*numcolours];
|
|
uint8_t trns[numcolours];
|
|
for(int i=0;i<numcolours;i++)
|
|
{
|
|
plte[3*i+0]=palette[4*i+0];
|
|
plte[3*i+1]=palette[4*i+1];
|
|
plte[3*i+2]=palette[4*i+2];
|
|
trns[i]=palette[4*i+3];
|
|
}
|
|
[png addChunk:'PLTE' bytes:plte length:3*numcolours];
|
|
[png addChunk:'tRNS' bytes:trns length:numcolours];
|
|
}
|
|
else
|
|
{
|
|
uint8_t plte[3*numcolours];
|
|
[zh readBytes:3*numcolours toBuffer:plte];
|
|
[png addChunk:'PLTE' bytes:plte length:3*numcolours];
|
|
}
|
|
|
|
[png startIDAT];
|
|
int bytesperrow=(width+3)&~3;
|
|
for(int y=0;y<height;y++)
|
|
{
|
|
uint8_t row[bytesperrow];
|
|
[zh readBytes:bytesperrow toBuffer:row];
|
|
[png addIDATRow:row];
|
|
}
|
|
[png endIDAT];
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
CSZlibHandle *zh=[CSZlibHandle zlibHandleWithHandle:handle];
|
|
|
|
[png addIHDRWithWidth:width height:height bitDepth:8 colourType:2];
|
|
[png startIDAT];
|
|
|
|
int bytesperrow=(2*width+3)&~3;
|
|
for(int y=0;y<height;y++)
|
|
{
|
|
uint8_t row[bytesperrow];
|
|
uint8_t rgbrow[3*width];
|
|
[zh readBytes:bytesperrow toBuffer:row];
|
|
|
|
for(int x=0;x<width;x++)
|
|
{
|
|
uint16_t rgb=CSUInt16BE(&row[2*x]);
|
|
uint8_t r=(((rgb>>10)&0x1f)*0x21)>>2;
|
|
uint8_t g=(((rgb>>5)&0x1f)*0x21)>>2;
|
|
uint8_t b=(((rgb>>0)&0x1f)*0x21)>>2;
|
|
|
|
rgbrow[3*x+0]=r;
|
|
rgbrow[3*x+1]=g;
|
|
rgbrow[3*x+2]=b;
|
|
}
|
|
|
|
[png addIDATRow:rgbrow];
|
|
}
|
|
|
|
[png endIDAT];
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
{
|
|
CSZlibHandle *zh=[CSZlibHandle zlibHandleWithHandle:handle];
|
|
|
|
if(alpha) [png addIHDRWithWidth:width height:height bitDepth:8 colourType:6];
|
|
else [png addIHDRWithWidth:width height:height bitDepth:8 colourType:2];
|
|
|
|
[png startIDAT];
|
|
|
|
int bytesperrow=width*4;
|
|
for(int y=0;y<height;y++)
|
|
{
|
|
uint8_t row[bytesperrow];
|
|
[zh readBytes:bytesperrow toBuffer:row];
|
|
|
|
if(alpha)
|
|
{
|
|
for(int x=0;x<width;x++)
|
|
{
|
|
uint8_t a=row[4*x+0],r=row[4*x+1],g=row[4*x+2],b=row[4*x+3];
|
|
row[4*x+0]=r;
|
|
row[4*x+1]=g;
|
|
row[4*x+2]=b;
|
|
row[4*x+3]=a;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int x=0;x<width;x++)
|
|
{
|
|
uint8_t r=row[4*x+1],g=row[4*x+2],b=row[4*x+3];
|
|
row[3*x+0]=r;
|
|
row[3*x+1]=g;
|
|
row[3*x+2]=b;
|
|
}
|
|
}
|
|
|
|
[png addIDATRow:row];
|
|
}
|
|
|
|
[png endIDAT];
|
|
}
|
|
break;
|
|
|
|
case -1: // Special alpha channel format.
|
|
{
|
|
[png addIHDRWithWidth:width height:height bitDepth:8 colourType:0];
|
|
|
|
CSZlibHandle *zh=[CSZlibHandle zlibHandleWithHandle:handle];
|
|
|
|
[png startIDAT];
|
|
int bytesperrow=width;
|
|
for(int y=0;y<height;y++)
|
|
{
|
|
uint8_t row[bytesperrow];
|
|
// Kludge: Some alpha channels end (one byte) early.
|
|
// Just zero the buffer and accept short reads to work around it.
|
|
memset(row,0,sizeof(row));
|
|
[zh readAtMost:bytesperrow toBuffer:row];
|
|
[png addIDATRow:row];
|
|
}
|
|
[png endIDAT];
|
|
}
|
|
break;
|
|
}
|
|
|
|
[png addIEND];
|
|
|
|
return [png data];
|
|
}
|
|
|
|
-(NSString *)formatName { return @"SWF"; }
|
|
|
|
@end
|
|
|