swfから画像を抽出するコマンドラインアプリケーション
リビジョン | 567e844c19e6f45dbcfab79a5cd5e895e77bb79a (tree) |
---|---|
日時 | 2018-05-21 23:04:29 |
作者 | Hori,Masaki <nandekka@popp...> |
コミッター | GitHub |
Object Oriented
@@ -9,6 +9,15 @@ | ||
9 | 9 | /* Begin PBXBuildFile section */ |
10 | 10 | F44C14BB1D84F07300ADE497 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F44C14BA1D84F07300ADE497 /* main.m */; }; |
11 | 11 | F44C14C41D85083E00ADE497 /* HMZlibData.m in Sources */ = {isa = PBXBuildFile; fileRef = F44C14C31D85083E00ADE497 /* HMZlibData.m */; }; |
12 | + F490569020B050D400CF637D /* BitsLossless2Decoder.m in Sources */ = {isa = PBXBuildFile; fileRef = F490568F20B050D400CF637D /* BitsLossless2Decoder.m */; }; | |
13 | + F490569320B0512C00CF637D /* Information.m in Sources */ = {isa = PBXBuildFile; fileRef = F490569220B0512C00CF637D /* Information.m */; }; | |
14 | + F490569620B0568F00CF637D /* ImageStorer.m in Sources */ = {isa = PBXBuildFile; fileRef = F490569520B0568F00CF637D /* ImageStorer.m */; }; | |
15 | + F490569A20B059B800CF637D /* BitLossless2ColorTableDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = F490569920B059B800CF637D /* BitLossless2ColorTableDecoder.m */; }; | |
16 | + F490569D20B05B9400CF637D /* BitsJPEG3Decoder.m in Sources */ = {isa = PBXBuildFile; fileRef = F490569C20B05B9400CF637D /* BitsJPEG3Decoder.m */; }; | |
17 | + F49056A320B0789B00CF637D /* SwfContent.m in Sources */ = {isa = PBXBuildFile; fileRef = F49056A220B0789B00CF637D /* SwfContent.m */; }; | |
18 | + F49056A620B078DB00CF637D /* SwfData.m in Sources */ = {isa = PBXBuildFile; fileRef = F49056A520B078DB00CF637D /* SwfData.m */; }; | |
19 | + F49056A920B0794400CF637D /* SwfHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = F49056A820B0794400CF637D /* SwfHeader.m */; }; | |
20 | + F49056B020B16A6D00CF637D /* BitsDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = F49056AF20B16A6D00CF637D /* BitsDecoder.m */; }; | |
12 | 21 | /* End PBXBuildFile section */ |
13 | 22 | |
14 | 23 | /* Begin PBXCopyFilesBuildPhase section */ |
@@ -29,6 +38,26 @@ | ||
29 | 38 | F44C14C11D84F27500ADE497 /* SWFStructure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWFStructure.h; sourceTree = "<group>"; }; |
30 | 39 | F44C14C21D85083E00ADE497 /* HMZlibData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMZlibData.h; sourceTree = "<group>"; }; |
31 | 40 | F44C14C31D85083E00ADE497 /* HMZlibData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMZlibData.m; sourceTree = "<group>"; }; |
41 | + F490568D20B04F5300CF637D /* ImageDecoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageDecoder.h; sourceTree = "<group>"; }; | |
42 | + F490568E20B050D400CF637D /* BitsLossless2Decoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitsLossless2Decoder.h; sourceTree = "<group>"; }; | |
43 | + F490568F20B050D400CF637D /* BitsLossless2Decoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BitsLossless2Decoder.m; sourceTree = "<group>"; }; | |
44 | + F490569120B0512C00CF637D /* Information.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Information.h; sourceTree = "<group>"; }; | |
45 | + F490569220B0512C00CF637D /* Information.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Information.m; sourceTree = "<group>"; }; | |
46 | + F490569420B0568F00CF637D /* ImageStorer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageStorer.h; sourceTree = "<group>"; }; | |
47 | + F490569520B0568F00CF637D /* ImageStorer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImageStorer.m; sourceTree = "<group>"; }; | |
48 | + F490569720B0584600CF637D /* KanColleGraphicDivider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KanColleGraphicDivider.h; sourceTree = "<group>"; }; | |
49 | + F490569820B059B800CF637D /* BitLossless2ColorTableDecoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitLossless2ColorTableDecoder.h; sourceTree = "<group>"; }; | |
50 | + F490569920B059B800CF637D /* BitLossless2ColorTableDecoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BitLossless2ColorTableDecoder.m; sourceTree = "<group>"; }; | |
51 | + F490569B20B05B9400CF637D /* BitsJPEG3Decoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitsJPEG3Decoder.h; sourceTree = "<group>"; }; | |
52 | + F490569C20B05B9400CF637D /* BitsJPEG3Decoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BitsJPEG3Decoder.m; sourceTree = "<group>"; }; | |
53 | + F49056A120B0789B00CF637D /* SwfContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwfContent.h; sourceTree = "<group>"; }; | |
54 | + F49056A220B0789B00CF637D /* SwfContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SwfContent.m; sourceTree = "<group>"; }; | |
55 | + F49056A420B078DB00CF637D /* SwfData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwfData.h; sourceTree = "<group>"; }; | |
56 | + F49056A520B078DB00CF637D /* SwfData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SwfData.m; sourceTree = "<group>"; }; | |
57 | + F49056A720B0794400CF637D /* SwfHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwfHeader.h; sourceTree = "<group>"; }; | |
58 | + F49056A820B0794400CF637D /* SwfHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SwfHeader.m; sourceTree = "<group>"; }; | |
59 | + F49056AE20B16A6C00CF637D /* BitsDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitsDecoder.h; sourceTree = "<group>"; }; | |
60 | + F49056AF20B16A6D00CF637D /* BitsDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BitsDecoder.m; sourceTree = "<group>"; }; | |
32 | 61 | /* End PBXFileReference section */ |
33 | 62 | |
34 | 63 | /* Begin PBXFrameworksBuildPhase section */ |
@@ -61,7 +90,27 @@ | ||
61 | 90 | F44C14B91D84F07300ADE497 /* KanColleGraphicDivider */ = { |
62 | 91 | isa = PBXGroup; |
63 | 92 | children = ( |
93 | + F490569720B0584600CF637D /* KanColleGraphicDivider.h */, | |
64 | 94 | F44C14BA1D84F07300ADE497 /* main.m */, |
95 | + F49056A420B078DB00CF637D /* SwfData.h */, | |
96 | + F49056A520B078DB00CF637D /* SwfData.m */, | |
97 | + F49056A720B0794400CF637D /* SwfHeader.h */, | |
98 | + F49056A820B0794400CF637D /* SwfHeader.m */, | |
99 | + F49056A120B0789B00CF637D /* SwfContent.h */, | |
100 | + F49056A220B0789B00CF637D /* SwfContent.m */, | |
101 | + F490569120B0512C00CF637D /* Information.h */, | |
102 | + F490569220B0512C00CF637D /* Information.m */, | |
103 | + F490569420B0568F00CF637D /* ImageStorer.h */, | |
104 | + F490569520B0568F00CF637D /* ImageStorer.m */, | |
105 | + F490568D20B04F5300CF637D /* ImageDecoder.h */, | |
106 | + F49056AE20B16A6C00CF637D /* BitsDecoder.h */, | |
107 | + F49056AF20B16A6D00CF637D /* BitsDecoder.m */, | |
108 | + F490568E20B050D400CF637D /* BitsLossless2Decoder.h */, | |
109 | + F490568F20B050D400CF637D /* BitsLossless2Decoder.m */, | |
110 | + F490569820B059B800CF637D /* BitLossless2ColorTableDecoder.h */, | |
111 | + F490569920B059B800CF637D /* BitLossless2ColorTableDecoder.m */, | |
112 | + F490569B20B05B9400CF637D /* BitsJPEG3Decoder.h */, | |
113 | + F490569C20B05B9400CF637D /* BitsJPEG3Decoder.m */, | |
65 | 114 | F44C14C11D84F27500ADE497 /* SWFStructure.h */, |
66 | 115 | F44C14C21D85083E00ADE497 /* HMZlibData.h */, |
67 | 116 | F44C14C31D85083E00ADE497 /* HMZlibData.m */, |
@@ -125,8 +174,17 @@ | ||
125 | 174 | isa = PBXSourcesBuildPhase; |
126 | 175 | buildActionMask = 2147483647; |
127 | 176 | files = ( |
177 | + F490569620B0568F00CF637D /* ImageStorer.m in Sources */, | |
178 | + F490569020B050D400CF637D /* BitsLossless2Decoder.m in Sources */, | |
179 | + F49056A320B0789B00CF637D /* SwfContent.m in Sources */, | |
180 | + F49056B020B16A6D00CF637D /* BitsDecoder.m in Sources */, | |
181 | + F490569A20B059B800CF637D /* BitLossless2ColorTableDecoder.m in Sources */, | |
128 | 182 | F44C14BB1D84F07300ADE497 /* main.m in Sources */, |
183 | + F49056A920B0794400CF637D /* SwfHeader.m in Sources */, | |
184 | + F490569D20B05B9400CF637D /* BitsJPEG3Decoder.m in Sources */, | |
185 | + F490569320B0512C00CF637D /* Information.m in Sources */, | |
129 | 186 | F44C14C41D85083E00ADE497 /* HMZlibData.m in Sources */, |
187 | + F49056A620B078DB00CF637D /* SwfData.m in Sources */, | |
130 | 188 | ); |
131 | 189 | runOnlyForDeploymentPostprocessing = 0; |
132 | 190 | }; |
@@ -249,6 +307,7 @@ | ||
249 | 307 | F44C14C01D84F07300ADE497 /* Release */, |
250 | 308 | ); |
251 | 309 | defaultConfigurationIsVisible = 0; |
310 | + defaultConfigurationName = Release; | |
252 | 311 | }; |
253 | 312 | /* End XCConfigurationList section */ |
254 | 313 | }; |
@@ -0,0 +1,8 @@ | ||
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
3 | +<plist version="1.0"> | |
4 | +<dict> | |
5 | + <key>IDEDidComputeMac32BitWarning</key> | |
6 | + <true/> | |
7 | +</dict> | |
8 | +</plist> |
@@ -0,0 +1,15 @@ | ||
1 | +// | |
2 | +// BitLossless2ColorTableDecoder.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | +#import "ImageDecoder.h" | |
12 | + | |
13 | +@interface BitLossless2ColorTableDecoder : NSObject <ImageDecoder> | |
14 | + | |
15 | +@end |
@@ -0,0 +1,114 @@ | ||
1 | +// | |
2 | +// BitLossless2ColorTableDecoder.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "BitLossless2ColorTableDecoder.h" | |
10 | + | |
11 | +#include "KanColleGraphicDivider.h" | |
12 | +#import "ImageStorer.h" | |
13 | + | |
14 | +#import "HMZlibData.h" | |
15 | + | |
16 | +@interface BitLossless2ColorTableDecoder() | |
17 | + | |
18 | +@property NSData *data; | |
19 | + | |
20 | +@property (readonly) NSUInteger length; | |
21 | + | |
22 | +@end | |
23 | + | |
24 | +@implementation BitLossless2ColorTableDecoder | |
25 | + | |
26 | ++ (instancetype)decoderWithData:(NSData *)data { | |
27 | + | |
28 | + return [[self alloc] initWithData:data]; | |
29 | +} | |
30 | + | |
31 | +- (instancetype)initWithData:(NSData *)data { | |
32 | + | |
33 | + self = [super init]; | |
34 | + | |
35 | + if( self ) { | |
36 | + | |
37 | + self.data = data; | |
38 | + } | |
39 | + | |
40 | + return self; | |
41 | +} | |
42 | + | |
43 | +- (NSUInteger)length { | |
44 | + | |
45 | + return self.data.length; | |
46 | +} | |
47 | + | |
48 | +- (void)decodeUsingInformationn:(Information *)information { | |
49 | + | |
50 | + saveDataWithExtension(information, self.decodedData, self.extension, self.charactorID); | |
51 | +} | |
52 | + | |
53 | +- (UInt32) charactorID { | |
54 | + | |
55 | + const HMSWFBitsLossless2 *data = (HMSWFBitsLossless2 *)self.data.bytes; | |
56 | + | |
57 | + return data->charctorID; | |
58 | +} | |
59 | + | |
60 | +- (NSData *)decodedData { | |
61 | + | |
62 | + const HMSWFBitsLossless2 *data = (HMSWFBitsLossless2 *)self.data.bytes; | |
63 | + | |
64 | + UInt8 mapSize = data->data.colorTable.colorTableSize + 1; | |
65 | + | |
66 | + NSData *zipedContentData = [NSData dataWithBytes:&data->data.colorTable.data length:self.length - HMSWFLossless2ColorTableHeaderSize]; | |
67 | + NSData *contentData = [zipedContentData inflate]; | |
68 | + | |
69 | + const UInt32 *mapP = (UInt32 *)contentData.bytes; | |
70 | + const UInt8 *colorIndexP = (UInt8 *)(mapP + mapSize); | |
71 | + | |
72 | + // rowサイズは4bytesアライメント | |
73 | + UInt8 skipBytes = data->width % 4; | |
74 | + | |
75 | + // ARGBカラーマップからARGBビットマップを作成 | |
76 | + UInt32 *imageDataP = calloc(4, data->width * data->height); | |
77 | + if(!imageDataP) { | |
78 | + fprintf(stderr, "Can not allocate enough memory.\n"); | |
79 | + return nil; | |
80 | + } | |
81 | + | |
82 | + UInt32 *imageDataPixel = imageDataP; | |
83 | + for(UInt16 h = 0; h < data->height; h++) { | |
84 | + for(UInt16 w = 0; w < data->width; w++) { | |
85 | + *imageDataPixel++ = mapP[*colorIndexP++]; | |
86 | + } | |
87 | + colorIndexP += skipBytes; | |
88 | + } | |
89 | + | |
90 | + // ARGBビットマップからNSBitmapImageRepを作成 | |
91 | + NSData *imageData = [NSData dataWithBytes:imageDataP length:4 * data->width * data->height]; | |
92 | + unsigned char *pp = (unsigned char *)imageData.bytes; | |
93 | + NSBitmapImageRep *imageRef = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pp | |
94 | + pixelsWide:data->width | |
95 | + pixelsHigh:data->height | |
96 | + bitsPerSample:8 | |
97 | + samplesPerPixel:4 | |
98 | + hasAlpha:YES | |
99 | + isPlanar:NO | |
100 | + colorSpaceName:NSCalibratedRGBColorSpace | |
101 | + bytesPerRow:data->width * 4 | |
102 | + bitsPerPixel:0]; | |
103 | + free(imageDataP); | |
104 | + imageDataP = NULL; | |
105 | + | |
106 | + return convertImagaData(imageRef); | |
107 | +} | |
108 | + | |
109 | +- (NSString *)extension { | |
110 | + | |
111 | + return @"png"; | |
112 | +} | |
113 | + | |
114 | +@end |
@@ -0,0 +1,15 @@ | ||
1 | +// | |
2 | +// BitsDecoder.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | +#import "ImageDecoder.h" | |
12 | + | |
13 | +@interface BitsDecoder : NSObject <ImageDecoder> | |
14 | + | |
15 | +@end |
@@ -0,0 +1,74 @@ | ||
1 | +// | |
2 | +// BitsDecoder.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "BitsDecoder.h" | |
10 | + | |
11 | +#import "ImageStorer.h" | |
12 | + | |
13 | +@interface BitsDecoder() | |
14 | + | |
15 | +@property NSData *data; | |
16 | + | |
17 | +@property (readonly) NSUInteger length; | |
18 | + | |
19 | +@end | |
20 | + | |
21 | +@implementation BitsDecoder | |
22 | + | |
23 | ++ (instancetype)decoderWithData:(NSData *)data { | |
24 | + | |
25 | + return [[self alloc] initWithData:data]; | |
26 | +} | |
27 | + | |
28 | +- (instancetype)initWithData:(NSData *)data { | |
29 | + | |
30 | + self = [super init]; | |
31 | + | |
32 | + if( self ) { | |
33 | + | |
34 | + self.data = data; | |
35 | + } | |
36 | + | |
37 | + return self; | |
38 | +} | |
39 | + | |
40 | +- (NSUInteger)length { | |
41 | + | |
42 | + return self.data.length; | |
43 | +} | |
44 | + | |
45 | +- (void)decodeUsingInformationn:(Information *)information { | |
46 | + | |
47 | + saveDataWithExtension(information, self.decodedData, self.extension, self.charactorID); | |
48 | +} | |
49 | + | |
50 | +- (UInt32) charactorID { | |
51 | + | |
52 | + return *(UInt16 *)self.data.bytes; | |
53 | +} | |
54 | + | |
55 | +- (NSData *)decodedData { | |
56 | + | |
57 | + NSData *contentData = [self.data subdataWithRange:NSMakeRange(2, self.length - 2)]; | |
58 | + if(contentData.length == 0) return nil; | |
59 | + | |
60 | + NSImage *pict = [[NSImage alloc] initWithData:contentData]; | |
61 | + if(!pict) { | |
62 | + fprintf(stderr, "Can not create image from data.\n"); | |
63 | + return nil; | |
64 | + } | |
65 | + | |
66 | + return contentData; | |
67 | +} | |
68 | + | |
69 | +- (NSString *)extension { | |
70 | + | |
71 | + return @"jpg"; | |
72 | +} | |
73 | + | |
74 | +@end |
@@ -0,0 +1,15 @@ | ||
1 | +// | |
2 | +// BitsJPEG3Decoder.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | +#import "ImageDecoder.h" | |
12 | + | |
13 | +@interface BitsJPEG3Decoder : NSObject <ImageDecoder> | |
14 | + | |
15 | +@end |
@@ -0,0 +1,136 @@ | ||
1 | +// | |
2 | +// BitsJPEG3Decoder.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "BitsJPEG3Decoder.h" | |
10 | + | |
11 | +#include "KanColleGraphicDivider.h" | |
12 | +#import "ImageStorer.h" | |
13 | + | |
14 | +#import "BitsDecoder.h" | |
15 | + | |
16 | +#import "HMZlibData.h" | |
17 | + | |
18 | +@interface BitsJPEG3Decoder() | |
19 | + | |
20 | +@property NSData *data; | |
21 | + | |
22 | +@property (readonly) NSUInteger length; | |
23 | + | |
24 | +@end | |
25 | + | |
26 | +@implementation BitsJPEG3Decoder | |
27 | + | |
28 | ++ (instancetype)decoderWithData:(NSData *)data { | |
29 | + | |
30 | + return [[self alloc] initWithData:data]; | |
31 | +} | |
32 | + | |
33 | +- (instancetype)initWithData:(NSData *)data { | |
34 | + | |
35 | + self = [super init]; | |
36 | + | |
37 | + if( self ) { | |
38 | + | |
39 | + self.data = data; | |
40 | + } | |
41 | + | |
42 | + return self; | |
43 | +} | |
44 | + | |
45 | +- (NSUInteger)length { | |
46 | + | |
47 | + return self.data.length; | |
48 | +} | |
49 | + | |
50 | +- (void)decodeUsingInformationn:(Information *)information { | |
51 | + | |
52 | + saveDataWithExtension(information, self.decodedData, self.extension, self.charactorID); | |
53 | +} | |
54 | + | |
55 | +- (UInt32) charactorID { | |
56 | + | |
57 | + const HMSWFBitsJPEG3 *data = (HMSWFBitsJPEG3 *)self.data.bytes; | |
58 | + | |
59 | + return data->charctorID; | |
60 | +} | |
61 | + | |
62 | +- (NSData *)decodedData { | |
63 | + | |
64 | + const unsigned char *p = self.data.bytes; | |
65 | + | |
66 | + if(self.length < HMSWFJPEG3HeaderSize) return nil; | |
67 | + | |
68 | + const HMSWFBitsJPEG3 *bitsJPEG3 = (HMSWFBitsJPEG3 *)p; | |
69 | + | |
70 | + NSUInteger contentLength = self.length - HMSWFJPEG3HeaderSize; | |
71 | + NSUInteger imageSize = bitsJPEG3->imageSize; | |
72 | + p = &bitsJPEG3->imageData; | |
73 | + | |
74 | + if(imageSize == contentLength) { | |
75 | + | |
76 | + return [[BitsDecoder decoderWithData:self.data] object]; | |
77 | + } | |
78 | + | |
79 | + // JPEGを取出し | |
80 | + NSData *pic = [NSData dataWithBytes:p length:imageSize]; | |
81 | + NSImage *pict = [[NSImage alloc] initWithData:pic]; | |
82 | + if(!pict) { | |
83 | + fprintf(stderr, "Can not create image from data.\n"); | |
84 | + return nil; | |
85 | + } | |
86 | + | |
87 | + NSSize size = pict.size; | |
88 | + | |
89 | + // アルファチャンネルの取出し | |
90 | + NSData *alpha = [NSData dataWithBytes:p + imageSize length:contentLength - imageSize]; | |
91 | + alpha = [alpha inflate]; | |
92 | + | |
93 | + unsigned char *pp = (unsigned char *)alpha.bytes; | |
94 | + NSBitmapImageRep *alphaImageRef = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pp | |
95 | + pixelsWide:size.width | |
96 | + pixelsHigh:size.height | |
97 | + bitsPerSample:8 | |
98 | + samplesPerPixel:1 | |
99 | + hasAlpha:NO | |
100 | + isPlanar:NO | |
101 | + colorSpaceName:NSDeviceWhiteColorSpace | |
102 | + bytesPerRow:size.width | |
103 | + bitsPerPixel:0]; | |
104 | + if(!alphaImageRef) { | |
105 | + fprintf(stderr, "Can not create alpha image from data.\n"); | |
106 | + return nil; | |
107 | + } | |
108 | + | |
109 | + // 透過画像の作成 | |
110 | + NSImage *image = [NSImage imageWithSize:size | |
111 | + flipped:NO | |
112 | + drawingHandler: | |
113 | + ^BOOL(NSRect dstRect) { | |
114 | + NSRect rect = NSMakeRect(0, 0, size.width, size.height); | |
115 | + | |
116 | + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; | |
117 | + CGContextSaveGState(context); | |
118 | + CGContextClipToMask(context, NSRectToCGRect(rect), alphaImageRef.CGImage); | |
119 | + [pict drawAtPoint:NSZeroPoint | |
120 | + fromRect:rect | |
121 | + operation:NSCompositeCopy | |
122 | + fraction:1.0]; | |
123 | + CGContextRestoreGState(context); | |
124 | + | |
125 | + return YES; | |
126 | + }]; | |
127 | + | |
128 | + return convertImagaData(image); | |
129 | +} | |
130 | + | |
131 | +- (NSString *)extension { | |
132 | + | |
133 | + return @"png"; | |
134 | +} | |
135 | + | |
136 | +@end |
@@ -0,0 +1,15 @@ | ||
1 | +// | |
2 | +// BitsLossless2Decoder.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | +#import "ImageDecoder.h" | |
12 | + | |
13 | +@interface BitsLossless2Decoder : NSObject <ImageDecoder> | |
14 | + | |
15 | +@end |
@@ -0,0 +1,97 @@ | ||
1 | +// | |
2 | +// BitsLossless2Decoder.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "BitsLossless2Decoder.h" | |
10 | + | |
11 | +#include "KanColleGraphicDivider.h" | |
12 | +#import "ImageStorer.h" | |
13 | + | |
14 | +#import "HMZlibData.h" | |
15 | + | |
16 | +#import "BitLossless2ColorTableDecoder.h" | |
17 | + | |
18 | +@interface BitsLossless2Decoder() | |
19 | + | |
20 | +@property NSData *data; | |
21 | + | |
22 | +@property (readonly) NSUInteger length; | |
23 | + | |
24 | +@end | |
25 | + | |
26 | +@implementation BitsLossless2Decoder | |
27 | + | |
28 | ++ (instancetype)decoderWithData:(NSData *)data { | |
29 | + | |
30 | + return [[self alloc] initWithData:data]; | |
31 | +} | |
32 | + | |
33 | +- (instancetype)initWithData:(NSData *)data { | |
34 | + | |
35 | + self = [super init]; | |
36 | + | |
37 | + if( self ) { | |
38 | + | |
39 | + self.data = data; | |
40 | + } | |
41 | + | |
42 | + return self; | |
43 | +} | |
44 | + | |
45 | +- (NSUInteger)length { | |
46 | + | |
47 | + return self.data.length; | |
48 | +} | |
49 | + | |
50 | +- (void)decodeUsingInformationn:(Information *)information { | |
51 | + | |
52 | + saveDataWithExtension(information, self.decodedData, self.extension, self.charactorID); | |
53 | +} | |
54 | + | |
55 | +- (UInt32) charactorID { | |
56 | + | |
57 | + const HMSWFBitsLossless2 *data = (HMSWFBitsLossless2 *)self.data.bytes; | |
58 | + | |
59 | + return data->charctorID; | |
60 | +} | |
61 | + | |
62 | +- (NSData *)decodedData { | |
63 | + | |
64 | + const HMSWFBitsLossless2 *data = (HMSWFBitsLossless2 *)self.data.bytes; | |
65 | + | |
66 | + if(data->bitmapFormat == 3) { | |
67 | + | |
68 | + id decoder = [BitLossless2ColorTableDecoder decoderWithData:self.data]; | |
69 | + return [decoder decodedData]; | |
70 | + } | |
71 | + | |
72 | + NSUInteger cLength = self.length - HMSWFLossless2HeaderSize; | |
73 | + | |
74 | + const unsigned char *p = &data->data.data; | |
75 | + NSData *zipedImageData = [NSData dataWithBytes:p length:cLength]; | |
76 | + NSData *imageData = [zipedImageData inflate]; | |
77 | + unsigned char *pp = (unsigned char *)imageData.bytes; | |
78 | + NSBitmapImageRep *imageRef = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pp | |
79 | + pixelsWide:data->width | |
80 | + pixelsHigh:data->height | |
81 | + bitsPerSample:8 * 4 | |
82 | + samplesPerPixel:4 | |
83 | + hasAlpha:YES | |
84 | + isPlanar:NO | |
85 | + colorSpaceName:NSCalibratedRGBColorSpace | |
86 | + bytesPerRow:data->width * 4 | |
87 | + bitsPerPixel:0]; | |
88 | + | |
89 | + return convertImagaData(imageRef); | |
90 | +} | |
91 | + | |
92 | +- (NSString *)extension { | |
93 | + | |
94 | + return @"png"; | |
95 | +} | |
96 | + | |
97 | +@end |
@@ -0,0 +1,21 @@ | ||
1 | +// | |
2 | +// ImageDecoder.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Foundation/Foundation.h> | |
10 | + | |
11 | +@protocol ImageDecoder <NSObject> | |
12 | + | |
13 | ++ (id)decoderWithData:(NSData *)data; | |
14 | + | |
15 | +@property (readonly) UInt32 charactorID; | |
16 | + | |
17 | +@property (nullable, readonly) NSData *decodedData; | |
18 | + | |
19 | +@property (readonly) NSString *extension; | |
20 | + | |
21 | +@end |
@@ -0,0 +1,14 @@ | ||
1 | +// | |
2 | +// ImageStorer.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | +#import "Information.h" | |
11 | + | |
12 | +void saveDataWithExtension(Information *info, id data, NSString *extention, UInt16 charactorID); | |
13 | + | |
14 | +NSData *convertImagaData(id data); |
@@ -0,0 +1,31 @@ | ||
1 | +// | |
2 | +// ImageStorer.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#include "KanColleGraphicDivider.h" | |
10 | +#import "ImageStorer.h" | |
11 | + | |
12 | +void saveDataWithExtension(Information *info, NSData *data, NSString *extention, UInt16 charactorID) { | |
13 | + | |
14 | + NSString *path = [NSString stringWithFormat:@"%@-%d.%@", info.originalName, charactorID, extention]; | |
15 | + path = [info.outputDir stringByAppendingPathComponent:path]; | |
16 | + NSURL *url = [NSURL fileURLWithPath:path]; | |
17 | + [data writeToURL:url atomically:YES]; | |
18 | +} | |
19 | + | |
20 | +NSData *convertImagaData(NSImage *image) { | |
21 | + | |
22 | + NSData *tiffData = [image TIFFRepresentation]; | |
23 | + if(!tiffData) { | |
24 | + fprintf(stderr, "Can not create TIFF representation.\n"); | |
25 | + return nil; | |
26 | + } | |
27 | + | |
28 | + NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithData:tiffData]; | |
29 | + return [rep representationUsingType:NSPNGFileType | |
30 | + properties:@{}]; | |
31 | +} |
@@ -0,0 +1,19 @@ | ||
1 | +// | |
2 | +// Information.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Foundation/Foundation.h> | |
10 | + | |
11 | +@interface Information: NSObject | |
12 | +@property (copy) NSString *originalName; | |
13 | +@property (copy) NSString *outputDir; | |
14 | +@property (copy) NSString *filename; | |
15 | +@property (copy) NSArray *charctorIds; | |
16 | + | |
17 | +- (bool)skipCharactorID:(UInt16) chractorid; | |
18 | + | |
19 | +@end |
@@ -0,0 +1,20 @@ | ||
1 | +// | |
2 | +// Information.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "Information.h" | |
10 | + | |
11 | +@implementation Information | |
12 | +- (bool)skipCharactorID:(UInt16) chractorid { | |
13 | + if(self.charctorIds.count == 0) return false; | |
14 | + | |
15 | + for(NSString *charID in self.charctorIds) { | |
16 | + if(charID.integerValue == chractorid) return false; | |
17 | + } | |
18 | + return true; | |
19 | +} | |
20 | +@end |
@@ -0,0 +1,21 @@ | ||
1 | +// | |
2 | +// KanColleGraphicDivider.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/19. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#ifndef KanColleGraphicDivider_h | |
10 | +#define KanColleGraphicDivider_h | |
11 | + | |
12 | +#include "SWFStructure.h" | |
13 | + | |
14 | +#if 0 | |
15 | +#define printLog(...) printLogF( __VA_ARGS__) | |
16 | +void printLogF(const char *fmt, ...); | |
17 | +#else | |
18 | +#define printLog(...) | |
19 | +#endif | |
20 | + | |
21 | +#endif /* KanColleGraphicDivider_h */ |
@@ -9,6 +9,7 @@ | ||
9 | 9 | #ifndef SWFStructure_h |
10 | 10 | #define SWFStructure_h |
11 | 11 | |
12 | +#import <MacTypes.h> | |
12 | 13 | |
13 | 14 | #pragma pack(1) |
14 | 15 |
@@ -0,0 +1,23 @@ | ||
1 | +// | |
2 | +// SwfContent.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Foundation/Foundation.h> | |
10 | + | |
11 | +@interface SwfContent : NSObject | |
12 | + | |
13 | ++ (nullable instancetype)contentWithData:(NSData *)data; | |
14 | + | |
15 | +@property NSRange nextRange; | |
16 | + | |
17 | +@property (nullable, readonly) SwfContent *next; | |
18 | + | |
19 | +@property (readonly) UInt32 charactorID; | |
20 | +@property (nullable, readonly) NSData *content; | |
21 | +@property (readonly) NSString *extension; | |
22 | + | |
23 | +@end |
@@ -0,0 +1,157 @@ | ||
1 | +// | |
2 | +// SwfContent.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "SwfContent.h" | |
10 | + | |
11 | +#import "BitsDecoder.h" | |
12 | +#import "BitsLossless2Decoder.h" | |
13 | +#import "BitsJPEG3Decoder.h" | |
14 | + | |
15 | +#include "SWFStructure.h" | |
16 | + | |
17 | +typedef enum : NSUInteger { | |
18 | + tagBits = 6, | |
19 | + tagJPEGTables = 8, // not supported | |
20 | + tagBitsJPEG2 = 21, // not supported | |
21 | + tagBitsJPEG3 = 35, | |
22 | + tagBitsLossless = 20, // not supported | |
23 | + tagBitsLossless2 = 36, | |
24 | + tagBitsJPEG4 = 90, // not supported | |
25 | +} TagType; | |
26 | + | |
27 | +@interface SwfContent() | |
28 | + | |
29 | +@property (nonnull) NSData *data; | |
30 | + | |
31 | +//@property NSRange nextRange; | |
32 | + | |
33 | +@property TagType tagType; | |
34 | +@property (nullable) NSData *contentData; | |
35 | +@property (nullable, readwrite) id<ImageDecoder> decoder; | |
36 | + | |
37 | +@end | |
38 | + | |
39 | +@implementation SwfContent | |
40 | + | |
41 | ++ (nullable instancetype)contentWithData:(NSData *)data { | |
42 | + | |
43 | + return [[self alloc] initWithData:data]; | |
44 | + | |
45 | +} | |
46 | + | |
47 | +- (nullable instancetype)initWithData:(NSData *)data { | |
48 | + | |
49 | + self = [super init]; | |
50 | + | |
51 | + if( !data ) { | |
52 | + | |
53 | + NSLog(@"SwfContent: Data is nil"); | |
54 | + | |
55 | + return nil; | |
56 | + } | |
57 | + | |
58 | + if( self ) { | |
59 | + | |
60 | + self.data = data; | |
61 | + self.nextRange = NSMakeRange(NSNotFound, 0); | |
62 | + | |
63 | + if( ![self parse] ) { | |
64 | + | |
65 | + NSLog(@"SwfContent: Parse Error"); | |
66 | + | |
67 | + return nil; | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + return self; | |
72 | +} | |
73 | + | |
74 | +- (SwfContent *)next { | |
75 | + | |
76 | + if( self.nextRange.location == NSNotFound ) { | |
77 | + | |
78 | + return nil; | |
79 | + } | |
80 | + | |
81 | + if( self.data.length < NSMaxRange(self.nextRange) ) { | |
82 | + | |
83 | + NSLog(@"Next size is Overflow"); | |
84 | + exit(-10); | |
85 | + } | |
86 | + | |
87 | + return [SwfContent contentWithData:[self.data subdataWithRange:self.nextRange]]; | |
88 | +} | |
89 | + | |
90 | +- (UInt32)charactorID { | |
91 | + | |
92 | + return self.decoder.charactorID; | |
93 | +} | |
94 | + | |
95 | +- (NSData *)content { | |
96 | + | |
97 | + return self.decoder.decodedData; | |
98 | +} | |
99 | + | |
100 | +- (NSString *)extension { | |
101 | + | |
102 | + return self.decoder.extension; | |
103 | +} | |
104 | + | |
105 | +- (BOOL)parse { | |
106 | + | |
107 | + HMSWFTag *tagP = (HMSWFTag *)self.data.bytes; | |
108 | + NSUInteger tagLength = 2; | |
109 | + self.tagType = tagP->tagAndLength >> 6; | |
110 | + UInt32 contentLength = tagP->tagAndLength & 0x3F; | |
111 | + if(contentLength == 0x3F) { | |
112 | + contentLength = tagP->extraLength; | |
113 | + tagLength += 4; | |
114 | + } | |
115 | + | |
116 | + if( self.tagType == 0 ) { | |
117 | + | |
118 | + return YES; | |
119 | + } | |
120 | + | |
121 | + if( self.data.length < (tagLength + contentLength) ) { | |
122 | + | |
123 | + NSLog(@"Content size is Overflow"); | |
124 | + exit(-10); | |
125 | + } | |
126 | + self.contentData = [self.data subdataWithRange:NSMakeRange(tagLength, contentLength)]; | |
127 | + | |
128 | + NSUInteger nextPos = tagLength + contentLength; | |
129 | + NSUInteger length = self.data.length - nextPos; | |
130 | + self.nextRange = NSMakeRange(nextPos, length); | |
131 | + | |
132 | + switch (self.tagType) { | |
133 | + | |
134 | + case tagBits: | |
135 | + | |
136 | + self.decoder = [BitsDecoder decoderWithData:self.contentData]; | |
137 | + break; | |
138 | + | |
139 | + case tagBitsJPEG3: | |
140 | + | |
141 | + self.decoder = [BitsJPEG3Decoder decoderWithData:self.contentData]; | |
142 | + break; | |
143 | + | |
144 | + case tagBitsLossless2: | |
145 | + | |
146 | + self.decoder = [BitsLossless2Decoder decoderWithData:self.contentData]; | |
147 | + break; | |
148 | + | |
149 | + default: | |
150 | + | |
151 | + break; | |
152 | + } | |
153 | + | |
154 | + return YES; | |
155 | +} | |
156 | + | |
157 | +@end |
@@ -0,0 +1,21 @@ | ||
1 | +// | |
2 | +// SwfData.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Foundation/Foundation.h> | |
10 | + | |
11 | +#import "SwfHeader.h" | |
12 | +#import "SwfContent.h" | |
13 | + | |
14 | +@interface SwfData : NSObject | |
15 | + | |
16 | ++ (instancetype)dataWithData:(NSData *)data; | |
17 | + | |
18 | +@property (nullable, readonly) SwfHeader *header; | |
19 | +@property (nullable, readonly) SwfContent *firstContent; | |
20 | + | |
21 | +@end |
@@ -0,0 +1,37 @@ | ||
1 | +// | |
2 | +// SwfData.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "SwfData.h" | |
10 | + | |
11 | +@interface SwfData() | |
12 | + | |
13 | +@property (nullable, readwrite) SwfHeader *header; | |
14 | +@property (nullable, readwrite) SwfContent *firstContent; | |
15 | + | |
16 | +@end | |
17 | + | |
18 | +@implementation SwfData | |
19 | + | |
20 | ++ (instancetype)dataWithData:(NSData *)data { | |
21 | + | |
22 | + return [[self alloc] initWithData:data]; | |
23 | +} | |
24 | + | |
25 | +- (instancetype)initWithData:(NSData *)data { | |
26 | + | |
27 | + self = [super init]; | |
28 | + if( self ) { | |
29 | + | |
30 | + self.header = [SwfHeader headerWithData:data]; | |
31 | + self.firstContent = [SwfContent contentWithData:self.header.next]; | |
32 | + } | |
33 | + | |
34 | + return self; | |
35 | +} | |
36 | + | |
37 | +@end |
@@ -0,0 +1,17 @@ | ||
1 | +// | |
2 | +// SwfHeader.h | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Foundation/Foundation.h> | |
10 | + | |
11 | +@interface SwfHeader : NSObject | |
12 | + | |
13 | ++ (nullable instancetype)headerWithData:(NSData *)data; | |
14 | + | |
15 | +@property (nullable, readonly) NSData *next; | |
16 | + | |
17 | +@end |
@@ -0,0 +1,146 @@ | ||
1 | +// | |
2 | +// SwfHeader.m | |
3 | +// KanColleGraphicDivider | |
4 | +// | |
5 | +// Created by Hori,Masaki on 2018/05/20. | |
6 | +// Copyright © 2018年 Hori,Masaki. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "SwfHeader.h" | |
10 | + | |
11 | +#import "HMZlibData.h" | |
12 | + | |
13 | +typedef enum : NSUInteger { | |
14 | + | |
15 | + normalSWFType, | |
16 | + zlibCompressedSWFType, | |
17 | + LZMACompressedSWFType, | |
18 | + | |
19 | + unknownSWFType, | |
20 | + | |
21 | +} SwfType; | |
22 | + | |
23 | + | |
24 | +@interface SwfHeader() | |
25 | + | |
26 | +@property (nonnull) NSData *data; | |
27 | + | |
28 | +@property (nullable, readwrite) NSData *next; | |
29 | + | |
30 | +@end | |
31 | + | |
32 | +@implementation SwfHeader | |
33 | + | |
34 | ++ (nullable instancetype)headerWithData:(NSData *)data { | |
35 | + | |
36 | + return [[self alloc] initWithData:data]; | |
37 | +} | |
38 | + | |
39 | +- (nullable instancetype)initWithData:(NSData *)data { | |
40 | + | |
41 | + self = [super init]; | |
42 | + | |
43 | + if( !data ) { | |
44 | + | |
45 | + NSLog(@"SwfHeader: Data is nil."); | |
46 | + | |
47 | + return nil; | |
48 | + } | |
49 | + | |
50 | + if( self ) { | |
51 | + | |
52 | + self.data = data; | |
53 | + | |
54 | + if( ![self parse] ) { | |
55 | + | |
56 | + NSLog(@"SwfHeader: Parse error"); | |
57 | + | |
58 | + return nil; | |
59 | + } | |
60 | + } | |
61 | + | |
62 | + return self; | |
63 | +} | |
64 | + | |
65 | +- (BOOL)parse { | |
66 | + | |
67 | + SwfType type = typeOf(self.data); | |
68 | + | |
69 | + switch (type) { | |
70 | + | |
71 | + case normalSWFType: | |
72 | + { | |
73 | + NSData *subdata = [self.data subdataWithRange:NSMakeRange(8, self.data.length - 8)]; | |
74 | + self.data = subdata; | |
75 | + } | |
76 | + break; | |
77 | + | |
78 | + case zlibCompressedSWFType: | |
79 | + { | |
80 | + NSData *subdata = [self.data subdataWithRange:NSMakeRange(8, self.data.length - 8)]; | |
81 | + self.data = [subdata inflate]; | |
82 | + } | |
83 | + break; | |
84 | + | |
85 | + case LZMACompressedSWFType: | |
86 | + { | |
87 | + NSData *subdata = [self.data subdataWithRange:NSMakeRange(8, self.data.length - 8)]; | |
88 | + self.data = [subdata inflate]; | |
89 | + } | |
90 | + break; | |
91 | + | |
92 | + default: | |
93 | + | |
94 | + NSLog(@"Signature is Unknown."); | |
95 | + | |
96 | + return NO; | |
97 | + } | |
98 | + | |
99 | + // RECT: 上位5bitsが各要素のサイズを表す 要素は4つ | |
100 | + UInt8 size = *(UInt8 *)self.data.bytes; | |
101 | + size >>= 3; | |
102 | + int offset = size * 4; | |
103 | + offset += 5; // 上位5bit分 | |
104 | + // bit -> byte | |
105 | + int div = offset / 8; | |
106 | + int mod = offset % 8; | |
107 | + offset = div + (mod == 0 ? 0 : 1); // アライメント | |
108 | + | |
109 | + // fps: 8.8 fixed number. | |
110 | + // 2 bytes. | |
111 | + | |
112 | + // frame count | |
113 | + // 2 bytes. | |
114 | + | |
115 | + NSUInteger contentLoc = offset + 2 + 2; | |
116 | + NSUInteger contentLength = self.data.length - contentLoc; | |
117 | + | |
118 | + NSRange contentRange = NSMakeRange(contentLoc, contentLength); | |
119 | + | |
120 | + self.next = [self.data subdataWithRange:contentRange]; | |
121 | + | |
122 | + return YES; | |
123 | +} | |
124 | + | |
125 | +SwfType typeOf(NSData *data) { | |
126 | + | |
127 | + NSString *signature = [[NSString alloc] initWithBytesNoCopy:(void *)data.bytes | |
128 | + length:3 | |
129 | + encoding:NSUTF8StringEncoding | |
130 | + freeWhenDone:NO]; | |
131 | + if( [signature isEqualToString:@"FWS"] ) { | |
132 | + | |
133 | + return normalSWFType; | |
134 | + } | |
135 | + if( [signature isEqualToString:@"CWS"] ) { | |
136 | + | |
137 | + return zlibCompressedSWFType; | |
138 | + } | |
139 | + if( [signature isEqualToString:@"ZWS"] ) { | |
140 | + | |
141 | + return LZMACompressedSWFType; | |
142 | + } | |
143 | + | |
144 | + return kUnknownType; | |
145 | +} | |
146 | +@end |
@@ -7,41 +7,22 @@ | ||
7 | 7 | // |
8 | 8 | |
9 | 9 | #import <Cocoa/Cocoa.h> |
10 | -#include "SWFStructure.h" | |
11 | -#import "HMZlibData.h" | |
12 | 10 | |
13 | 11 | #include <getopt.h> |
14 | 12 | |
13 | +#include "KanColleGraphicDivider.h" | |
15 | 14 | |
16 | -@interface Information: NSObject | |
17 | -@property (copy) NSString *originalName; | |
18 | -@property (copy) NSString *outputDir; | |
19 | -@property (copy) NSString *filename; | |
20 | -@property (copy) NSArray *charctorIds; | |
21 | -@end | |
15 | +#import "Information.h" | |
16 | +#import "ImageStorer.h" | |
22 | 17 | |
23 | -@implementation Information | |
24 | -- (bool)skipCharactorID:(UInt16) chractorid { | |
25 | - if(self.charctorIds.count == 0) return false; | |
26 | - | |
27 | - for(NSString *charID in self.charctorIds) { | |
28 | - if(charID.integerValue == chractorid) return false; | |
29 | - } | |
30 | - return true; | |
31 | -} | |
32 | -@end | |
18 | +#import "SwfData.h" | |
33 | 19 | |
34 | -#if 0 | |
35 | -#define printLog(...) printLogF( __VA_ARGS__) | |
36 | 20 | void printLogF(const char *fmt, ...) { |
37 | 21 | va_list ap; |
38 | 22 | va_start(ap, fmt); |
39 | 23 | vfprintf(stderr, fmt, ap); |
40 | 24 | va_end(ap); |
41 | 25 | } |
42 | -#else | |
43 | -#define printLog(...) | |
44 | -#endif | |
45 | 26 | |
46 | 27 | void printHex(const unsigned char *p) { |
47 | 28 | for(int i=0;i<1;i++) { |
@@ -52,17 +33,6 @@ void printHex(const unsigned char *p) { | ||
52 | 33 | } |
53 | 34 | } |
54 | 35 | |
55 | -enum { | |
56 | - tagBits = 6, | |
57 | - tagJPEGTables = 8, // not supported | |
58 | - tagBitsJPEG2 = 21, // not supported | |
59 | - tagBitsJPEG3 = 35, | |
60 | - tagBitsLossless = 20, // not supported | |
61 | - tagBitsLossless2 = 36, | |
62 | - tagBitsJPEG4 = 90, // not supported | |
63 | - | |
64 | -}; | |
65 | - | |
66 | 36 | const char *toolName; |
67 | 37 | const char *versionString = "1.0"; |
68 | 38 |
@@ -94,221 +64,6 @@ static void version() | ||
94 | 64 | exit(EXIT_SUCCESS); |
95 | 65 | } |
96 | 66 | |
97 | -void saveDataWithExtension(Information *info, id data, NSString *extention, UInt16 charactorID) { | |
98 | - NSString *path = [NSString stringWithFormat:@"%@-%d.%@", info.originalName, charactorID, extention]; | |
99 | - path = [info.outputDir stringByAppendingPathComponent:path]; | |
100 | - NSURL *url = [NSURL fileURLWithPath:path]; | |
101 | - [data writeToURL:url atomically:YES]; | |
102 | -} | |
103 | - | |
104 | -void saveImageAsPNG(Information *info, id image, UInt16 charactorID) { | |
105 | - NSData *tiffData = [image TIFFRepresentation]; | |
106 | - if(!tiffData) { | |
107 | - fprintf(stderr, "Can not create TIFF representation.\n"); | |
108 | - return; | |
109 | - } | |
110 | - | |
111 | - NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithData:tiffData]; | |
112 | - NSData *imageData = [rep representationUsingType:NSPNGFileType | |
113 | - properties:@{}]; | |
114 | - saveDataWithExtension(info, imageData, @"png", charactorID); | |
115 | -} | |
116 | - | |
117 | -void storeImage(Information *info, const unsigned char *p, UInt32 length, UInt16 charactorID) { | |
118 | - printLog("#### TYPE IS PICTURE ####\n\n"); | |
119 | - | |
120 | - printLog("CaractorID is %d\n", charactorID); | |
121 | - if([info skipCharactorID:charactorID]) return; | |
122 | - | |
123 | - if(length == 0) return; | |
124 | - | |
125 | - NSData *pic = [NSData dataWithBytes:p length:length]; | |
126 | - NSImage *pict = [[NSImage alloc] initWithData:pic]; | |
127 | - if(!pict) { | |
128 | - fprintf(stderr, "Can not create image from data.\n"); | |
129 | - return; | |
130 | - } | |
131 | - | |
132 | - saveDataWithExtension(info, pic, @"jpg", charactorID); | |
133 | -} | |
134 | - | |
135 | -void storeBitsJPEG3(Information *info, const unsigned char *p, UInt32 length) { | |
136 | - printLog("#### TYPE IS PICTURE ####\n\n"); | |
137 | - if(length < HMSWFJPEG3HeaderSize) return; | |
138 | - | |
139 | - const HMSWFBitsJPEG3 *bitsJPEG3 = (HMSWFBitsJPEG3 *)p; | |
140 | - | |
141 | - UInt16 charactorID = bitsJPEG3->charctorID; | |
142 | - printLog("CaractorID is %d\n", charactorID); | |
143 | - if([info skipCharactorID:charactorID]) return; | |
144 | - | |
145 | - UInt32 contentLength = length - HMSWFJPEG3HeaderSize; | |
146 | - UInt32 imageSize = bitsJPEG3->imageSize; | |
147 | - p = &bitsJPEG3->imageData; | |
148 | - | |
149 | - if(imageSize == contentLength) { | |
150 | - storeImage(info, p, contentLength, charactorID); | |
151 | - return; | |
152 | - } | |
153 | - | |
154 | - // JPEGを取出し | |
155 | - NSData *pic = [NSData dataWithBytes:p length:imageSize]; | |
156 | - NSImage *pict = [[NSImage alloc] initWithData:pic]; | |
157 | - if(!pict) { | |
158 | - fprintf(stderr, "Can not create image from data.\n"); | |
159 | - return; | |
160 | - } | |
161 | - | |
162 | - NSSize size = pict.size; | |
163 | - | |
164 | - // アルファチャンネルの取出し | |
165 | - NSData *alpha = [NSData dataWithBytes:p + imageSize length:contentLength - imageSize]; | |
166 | - alpha = [alpha inflate]; | |
167 | - | |
168 | - unsigned char *pp = (unsigned char *)alpha.bytes; | |
169 | - NSBitmapImageRep *alphaImageRef = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pp | |
170 | - pixelsWide:size.width | |
171 | - pixelsHigh:size.height | |
172 | - bitsPerSample:8 | |
173 | - samplesPerPixel:1 | |
174 | - hasAlpha:NO | |
175 | - isPlanar:NO | |
176 | - colorSpaceName:NSDeviceWhiteColorSpace | |
177 | - bytesPerRow:size.width | |
178 | - bitsPerPixel:0]; | |
179 | - if(!alphaImageRef) { | |
180 | - fprintf(stderr, "Can not create alpha image from data.\n"); | |
181 | - return; | |
182 | - } | |
183 | - | |
184 | - // 透過画像の作成 | |
185 | - NSImage *image = [NSImage imageWithSize:size | |
186 | - flipped:NO | |
187 | - drawingHandler: | |
188 | - ^BOOL(NSRect dstRect) { | |
189 | - NSRect rect = NSMakeRect(0, 0, size.width, size.height); | |
190 | - | |
191 | - CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; | |
192 | - CGContextSaveGState(context); | |
193 | - CGContextClipToMask(context, NSRectToCGRect(rect), alphaImageRef.CGImage); | |
194 | - [pict drawAtPoint:NSZeroPoint | |
195 | - fromRect:rect | |
196 | - operation:NSCompositeCopy | |
197 | - fraction:1.0]; | |
198 | - CGContextRestoreGState(context); | |
199 | - | |
200 | - return YES; | |
201 | - }]; | |
202 | - | |
203 | - saveImageAsPNG(info, image, charactorID); | |
204 | -} | |
205 | - | |
206 | -void storeBitLossless2ColorTable(Information *info, const unsigned char *p, UInt32 length) { | |
207 | - const HMSWFBitsLossless2 *data = (HMSWFBitsLossless2 *)p; | |
208 | - | |
209 | - UInt16 charactorID = data->charctorID; | |
210 | - printLog("CaractorID is %d\n", charactorID); | |
211 | - if([info skipCharactorID:charactorID]) return; | |
212 | - | |
213 | - UInt8 mapSize = data->data.colorTable.colorTableSize + 1; | |
214 | - printLog("color table size -> %d\n", mapSize); | |
215 | - printLog("zipped image data size -> %d\n", length - HMSWFLossless2ColorTableHeaderSize); | |
216 | - | |
217 | - NSData *zipedContentData = [NSData dataWithBytes:&data->data.colorTable.data length:length - HMSWFLossless2ColorTableHeaderSize]; | |
218 | - NSData *contentData = [zipedContentData inflate]; | |
219 | - printLog("unzipped image data size -> %d\n", contentData.length); | |
220 | - | |
221 | - const UInt32 *mapP = (UInt32 *)contentData.bytes; | |
222 | - const UInt8 *colorIndexP = (UInt8 *)(mapP + mapSize); | |
223 | - | |
224 | -#if 0 | |
225 | - printLog("MAP TABLE\n"); | |
226 | - for(int i = 0; i < mapSize; i++) { | |
227 | - printLog("0x%04x ", mapP[i]); | |
228 | - } | |
229 | - printLog("\n\n"); | |
230 | -#endif | |
231 | - | |
232 | - // rowサイズは4bytesアライメント | |
233 | - UInt8 skipBytes = data->width % 4; | |
234 | - | |
235 | - // ARBGカラーマップからARBGビットマップを作成 | |
236 | - UInt32 *imageDataP = calloc(4, data->width * data->height); | |
237 | - if(!imageDataP) { | |
238 | - fprintf(stderr, "Can not allocate enough memory.\n"); | |
239 | - return; | |
240 | - } | |
241 | - | |
242 | - UInt32 *imageDataPixel = imageDataP; | |
243 | - for(UInt16 h = 0; h < data->height; h++) { | |
244 | - for(UInt16 w = 0; w < data->width; w++) { | |
245 | - printLog("%d ", *colorIndexP); | |
246 | - *imageDataPixel++ = mapP[*colorIndexP++]; | |
247 | - } | |
248 | - colorIndexP += skipBytes; | |
249 | - printLog("\n"); | |
250 | - } | |
251 | - | |
252 | - // ARGBビットマップからNSBitmapImageRepを作成 | |
253 | - NSData *imageData = [NSData dataWithBytes:imageDataP length:4 * data->width * data->height]; | |
254 | - unsigned char *pp = (unsigned char *)imageData.bytes; | |
255 | - NSBitmapImageRep *imageRef = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pp | |
256 | - pixelsWide:data->width | |
257 | - pixelsHigh:data->height | |
258 | - bitsPerSample:8 | |
259 | - samplesPerPixel:4 | |
260 | - hasAlpha:YES | |
261 | - isPlanar:NO | |
262 | - colorSpaceName:NSCalibratedRGBColorSpace | |
263 | - bytesPerRow:data->width * 4 | |
264 | - bitsPerPixel:0]; | |
265 | - free(imageDataP); | |
266 | - imageDataP = NULL; | |
267 | - | |
268 | - if(!imageRef) { | |
269 | - fprintf(stderr, "Can not create ImageRef from maked bitmap."); | |
270 | - return; | |
271 | - } | |
272 | - | |
273 | - saveImageAsPNG(info, imageRef, charactorID); | |
274 | -} | |
275 | -void storeBitsLossless2(Information *info, const unsigned char *p, UInt32 length) { | |
276 | - printLog("#### TYPE IS PICTURE ####\n\n"); | |
277 | - if(length < HMSWFLossless2HeaderSize) { | |
278 | - fprintf(stderr, "length is too short.\n"); | |
279 | - return; | |
280 | - } | |
281 | - | |
282 | - const HMSWFBitsLossless2 *data = (HMSWFBitsLossless2 *)p; | |
283 | - | |
284 | - UInt16 charactorID = data->charctorID; | |
285 | - printLog("CaractorID is %d\n", charactorID); | |
286 | - if([info skipCharactorID:charactorID]) return; | |
287 | - | |
288 | - if(data->bitmapFormat == 3) { | |
289 | - storeBitLossless2ColorTable(info, p, length); | |
290 | - return; | |
291 | - } | |
292 | - | |
293 | - length -= HMSWFLossless2HeaderSize; | |
294 | - | |
295 | - p = &data->data.data; | |
296 | - NSData *zipedImageData = [NSData dataWithBytes:p length:length]; | |
297 | - NSData *imageData = [zipedImageData inflate]; | |
298 | - unsigned char *pp = (unsigned char *)imageData.bytes; | |
299 | - NSBitmapImageRep *imageRef = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pp | |
300 | - pixelsWide:data->width | |
301 | - pixelsHigh:data->height | |
302 | - bitsPerSample:8 * 4 | |
303 | - samplesPerPixel:4 | |
304 | - hasAlpha:YES | |
305 | - isPlanar:NO | |
306 | - colorSpaceName:NSCalibratedRGBColorSpace | |
307 | - bytesPerRow:data->width * 4 | |
308 | - bitsPerPixel:0]; | |
309 | - saveImageAsPNG(info, imageRef, charactorID); | |
310 | -} | |
311 | - | |
312 | 67 | void extractImagesFromSWFFile(Information *info) { |
313 | 68 | NSString *filePath = info.filename; |
314 | 69 | if(![filePath hasPrefix:@"/"]) { |
@@ -326,109 +81,27 @@ void extractImagesFromSWFFile(Information *info) { | ||
326 | 81 | info.originalName = [filePath lastPathComponent]; |
327 | 82 | info.originalName = [info.originalName stringByDeletingPathExtension]; |
328 | 83 | |
329 | - printHex(data.bytes); | |
330 | - | |
331 | - // ヘッダの処理開始 | |
332 | - const HMSWFHeader *header = data.bytes; | |
333 | - printLog("type\t%c%c%c\n", header->type[0], header->type[1], header->type[2]); | |
334 | - printLog("version\t%d\n", header->version); | |
335 | - printLog("file size\t0x%x (%d)\n", header->f.f, header->f.f); | |
336 | - data = [data subdataWithRange:NSMakeRange(8, data.length - 8)]; | |
337 | - printHex(data.bytes); | |
338 | - | |
339 | - if(header->type[0] != 'F' && header->type[0] != 'C') { | |
340 | - fprintf(stderr, "File %s is not SWF.\n", info.filename.UTF8String); | |
341 | - return; | |
342 | - } | |
343 | - if(header->type[1] != 'W' || header->type[2] != 'S') { | |
344 | - fprintf(stderr, "File %s is not SWF.\n", info.filename.UTF8String); | |
345 | - return; | |
346 | - } | |
347 | - | |
348 | - // シグニチャがCの時はコンテントはzlibで圧縮されている | |
349 | - if(header->type[0] == 'C') { | |
350 | - data = [data inflate]; | |
351 | - printHex(data.bytes); | |
352 | - } | |
353 | - | |
354 | - const unsigned char *p = data.bytes; | |
355 | - | |
356 | - // RECT: 上位5bitsが各要素のサイズを表す 要素は4つ | |
357 | - UInt8 size = *(UInt8 *)p; | |
358 | - size >>= 3; | |
359 | - printLog("size -> %u (%x)\n", size, size); | |
360 | - int offset = size * 4; | |
361 | - offset += 5; // 上位5bit分 | |
362 | - // bit -> byte | |
363 | - int div = offset / 8; | |
364 | - int mod = offset % 8; | |
365 | - offset = div + (mod == 0 ? 0 : 1); // アライメント | |
366 | - printLog("offset -> %d\n", offset); | |
367 | - p += offset; | |
368 | - | |
369 | - // fps: 8.8 fixed number. | |
370 | - printLog("fps -> %u.%u\n", *(UInt8 *)(p + 1), *(UInt8 *)p); | |
371 | - p += 2; | |
84 | + SwfData *swf = [SwfData dataWithData:data]; | |
85 | + SwfContent *content = swf.firstContent; | |
372 | 86 | |
373 | - // frame count | |
374 | - printLog("frame count -> %u\n", *(UInt16 *)p); | |
375 | - p += 2; | |
376 | - | |
377 | - // タグの処理開始 | |
378 | - int tagCount = 0; | |
379 | - while(1) { | |
380 | - printHex(p); | |
381 | - | |
382 | - HMSWFTag *tagP = (HMSWFTag *)p; | |
383 | - p += 2; | |
384 | - printLog("tag and length -> 0x%04x\n", tagP->tagAndLength); | |
385 | - UInt32 tag = tagP->tagAndLength >> 6; | |
386 | - UInt32 length = tagP->tagAndLength & 0x3F; | |
387 | - if(length == 0x3F) { | |
388 | - length = tagP->extraLength; | |
389 | - p += 4; | |
390 | - } | |
391 | - printLog("tag -> %u\nlength -> %u\n", tag, length); | |
87 | + while( content ) { | |
392 | 88 | |
393 | - // tag == 0 終了タグ | |
394 | - if(tag == 0) break; | |
89 | + SwfContent *aContent = content; | |
90 | + content = aContent.next; | |
395 | 91 | |
396 | - // 画像の時の処理 | |
397 | - switch(tag) { | |
398 | - case tagBits: | |
399 | - @autoreleasepool { | |
400 | - storeImage(info, p + 2, length - 2, *(UInt16 *)p); | |
401 | - } | |
402 | - break; | |
403 | - case tagBitsJPEG3: | |
404 | - @autoreleasepool { | |
405 | - storeBitsJPEG3(info, p, length); | |
406 | - } | |
407 | - break; | |
408 | - case tagBitsLossless2: | |
409 | - @autoreleasepool { | |
410 | - storeBitsLossless2(info, p, length); | |
411 | - } | |
412 | - break; | |
413 | - case tagBitsJPEG2: | |
414 | - case tagBitsLossless: | |
415 | - case tagBitsJPEG4: | |
416 | - case tagJPEGTables: | |
417 | - if(length > 0) { | |
418 | - fprintf(stderr, "Not supported type. (tag=%d)\n", tag); | |
419 | - } | |
420 | - break; | |
92 | + NSData *data = aContent.content; | |
93 | + if( !data ) { | |
94 | + | |
95 | + continue; | |
421 | 96 | } |
422 | 97 | |
423 | - p += length; | |
424 | - tagCount++; | |
425 | - | |
426 | - if(tagCount > 200) { | |
427 | - return; | |
98 | + if( [info skipCharactorID:aContent.charactorID] ) { | |
99 | + | |
100 | + continue; | |
428 | 101 | } |
102 | + | |
103 | + saveDataWithExtension(info, data, aContent.extension, aContent.charactorID); | |
429 | 104 | } |
430 | - | |
431 | - printLog("tag Count -> %d\n", tagCount); | |
432 | 105 | } |
433 | 106 | |
434 | 107 | int main(int argc, char * const *argv) { |
@@ -446,11 +119,11 @@ int main(int argc, char * const *argv) { | ||
446 | 119 | |
447 | 120 | #define SHORTOPTS "ho:vc:" |
448 | 121 | static struct option longopts[] = { |
449 | - {"output", required_argument, NULL, 'o'}, | |
450 | - {"charactorid", required_argument, NULL, 'c'}, | |
451 | - {"version", no_argument, NULL, 'v'}, | |
452 | - {"help", no_argument, NULL, 'h'}, | |
453 | - {NULL, 0, NULL, 0} | |
122 | + {"output", required_argument, NULL, 'o'}, | |
123 | + {"charactorid", required_argument, NULL, 'c'}, | |
124 | + {"version", no_argument, NULL, 'v'}, | |
125 | + {"help", no_argument, NULL, 'h'}, | |
126 | + {NULL, 0, NULL, 0} | |
454 | 127 | }; |
455 | 128 | |
456 | 129 | while((opt = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) { |