mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
135 lines
4.6 KiB
Objective-C
135 lines
4.6 KiB
Objective-C
#import "ID3Artwork.h"
|
|
|
|
const uint8_t ID3v2[5] = {0x49, 0x44, 0x33, 0x02, 0x00};
|
|
const uint8_t ID3v3[5] = {0x49, 0x44, 0x33, 0x03, 0x00};
|
|
const NSUInteger ID3VersionOffset = 3;
|
|
const NSUInteger ID3SizeOffset = 6;
|
|
const NSUInteger ID3TagOffset = 10;
|
|
const NSUInteger ID3ArtOffset = 12;
|
|
|
|
const NSUInteger ID3v2FrameOffset = 6;
|
|
const NSUInteger ID3v3FrameOffset = 10;
|
|
|
|
const uint8_t ID3v2Artwork[3] = {0x50, 0x49, 0x43};
|
|
const uint8_t ID3v3Artwork[4] = {0x41, 0x50, 0x49, 0x43};
|
|
|
|
const uint8_t JPGMagic[3] = {0xff, 0xd8, 0xff};
|
|
const uint8_t PNGMagic[4] = {0x89, 0x50, 0x4e, 0x47};
|
|
|
|
uint32_t getSize(const uint8_t *bytes) {
|
|
uint32_t size = CFSwapInt32HostToBig(*(const uint32_t *)(bytes + ID3SizeOffset));
|
|
uint32_t b1 = (size & 0x7F000000) >> 3;
|
|
uint32_t b2 = (size & 0x007F0000) >> 2;
|
|
uint32_t b3 = (size & 0x00007F00) >> 1;
|
|
uint32_t b4 = size & 0x0000007F;
|
|
return b1 + b2 + b3 + b4;
|
|
}
|
|
|
|
uint32_t frameOffsetForVersion(uint8_t version) {
|
|
return version == 2 ? ID3v2FrameOffset : ID3v3FrameOffset;
|
|
}
|
|
|
|
uint32_t frameSizeForBytes(const uint8_t *framePtr, uint8_t version) {
|
|
uint8_t offset = version == 2 ? 2 : 4;
|
|
uint32_t size = CFSwapInt32HostToBig(*(uint32_t *)(framePtr + offset));
|
|
|
|
if (version == 2) {
|
|
size &= 0x00FFFFFF;
|
|
}
|
|
|
|
return size + frameOffsetForVersion(version);
|
|
}
|
|
|
|
bool isArtworkFrame(const uint8_t *framePtr, uint8_t version) {
|
|
if (version == 2) {
|
|
return memcmp(framePtr, ID3v2Artwork, 3) == 0;
|
|
}
|
|
|
|
return memcmp(framePtr, ID3v3Artwork, 4) == 0;
|
|
}
|
|
|
|
NSData * _Nullable albumArtworkData(NSData * _Nonnull data) {
|
|
if (data.length < 4) {
|
|
return nil;
|
|
}
|
|
|
|
const uint8_t *bytes = data.bytes;
|
|
if (!(memcmp(bytes, ID3v2, 5) == 0 || memcmp(bytes, ID3v3, 5) == 0)) {
|
|
return nil;
|
|
}
|
|
|
|
uint8_t version = bytes[ID3VersionOffset];
|
|
uint32_t size = CFSwapInt32HostToBig(*(const uint32_t *)(bytes + ID3SizeOffset));
|
|
uint32_t b1 = (size & 0x7F000000) >> 3;
|
|
uint32_t b2 = (size & 0x007F0000) >> 2;
|
|
uint32_t b3 = (size & 0x00007F00) >> 1;
|
|
uint32_t b4 = size & 0x0000007F;
|
|
size = b1 + b2 + b3 + b4;
|
|
|
|
const uint8_t *ptr = bytes + ID3TagOffset;
|
|
|
|
uint32_t pos = 0;
|
|
while (pos < size) {
|
|
const uint8_t * const frameBytes = ptr + pos;
|
|
uint32_t frameSize = frameSizeForBytes(frameBytes, version);
|
|
|
|
if (isArtworkFrame(frameBytes, version)) {
|
|
uint32_t frameOffset = frameOffsetForVersion(version);
|
|
const uint8_t *ptr = frameBytes + frameOffset;
|
|
|
|
bool isJpg = false;
|
|
uint32_t imageOffset = UINT32_MAX;
|
|
for (uint32_t i = 0; i < frameSize - 4; i++) {
|
|
if (memcmp(ptr + i, JPGMagic, 3) == 0) {
|
|
imageOffset = i;
|
|
isJpg = true;
|
|
break;
|
|
} else if (memcmp(ptr + i, PNGMagic, 4) == 0) {
|
|
imageOffset = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (imageOffset != UINT32_MAX) {
|
|
uint32_t start = ID3TagOffset + pos + frameOffset;
|
|
if (isJpg) {
|
|
NSMutableData *jpgData = [[NSMutableData alloc] initWithCapacity:frameSize + 1024];
|
|
uint8_t previousByte = 0xff;
|
|
uint32_t skippedBytes = 0;
|
|
|
|
for (uint32_t i = 0; i < frameSize - imageOffset + skippedBytes; i++) {
|
|
uint32_t offset = imageOffset + i;
|
|
if (start + offset >= data.length) {
|
|
return nil;
|
|
}
|
|
uint8_t byte = (uint8_t)ptr[offset];
|
|
// if (byte == 0x00 && previousByte == 0xff) {
|
|
// skippedBytes++;
|
|
// } else {
|
|
[jpgData appendBytes:&byte length:1];
|
|
// }
|
|
if (byte == 0xd9 && previousByte == 0xff) {
|
|
break;
|
|
}
|
|
previousByte = byte;
|
|
}
|
|
return jpgData;
|
|
}
|
|
else {
|
|
if (start + frameSize > data.length) {
|
|
return nil;
|
|
}
|
|
return [[NSData alloc] initWithBytes:ptr + imageOffset length:frameSize - imageOffset];
|
|
}
|
|
}
|
|
}
|
|
else if (frameBytes[0] == 0x00 && frameBytes[1] == 0x00 && frameBytes[2] == 0x00) {
|
|
break;
|
|
}
|
|
|
|
pos += frameSize;
|
|
}
|
|
|
|
return nil;
|
|
}
|