Kunal Jha
Kunal Jha

Reputation: 2152

Convert the C Function to Perl

I am stuck while trying to convert the C function convertCNGFileToJPGFile mentioned in the program cng2jpg.c

I have been trying to write the same in Perl but don't have enough knowhow with hex,pack and unpack functions.

Would really appreciate if somebody can write a similar code in Perl as mentioned below.

while ((bytesRead = fread(buffer, 1, kBufferSize, inputFile))) {
    if (!isValidCNG) {
        if (bytesRead < 11 || strncmp("\xa5\xa9\xa6\xa9", (char *)(buffer + 6), 4)) {
            fprintf(stderr, "%s does not appear to be a valid CNG file\n", inputFileName);
            return 0;
        }
        isValidCNG = 1;
    }
    for (size_t i = 0; i < bytesRead; i++)
        buffer[i] ^= 0xEF;
    size_t bytesWritten = fwrite(buffer, 1, bytesRead, outputFile);
    if (bytesWritten < bytesRead) {
        fprintf(stderr, "Error writing %s\n", outputFileName);
        return 0;
    }
}

Thanks in advance.

Upvotes: 1

Views: 205

Answers (1)

Ilmari Karonen
Ilmari Karonen

Reputation: 50368

If I'm reading the code right, all it's doing (besides the validity check) is XORing each byte in the file with the byte 0xEF (i.e. flipping all but the fifth lowest bit of each byte). In Perl, you could implement that with:

local $/ = \(2**16);  # ignore line breaks, read in 64 kiB chunks
while (<>) {
    $_ ^= "\xEF" x length;
    print;
}

The validity check is just checking that the output is actually a valid JPEG file — specifically, that the 7th to 10th bytes of the output file contain the magic word "JFIF" (which becomes "\xa5\xa9\xa6\xa9" when XORed with 0xEF). Generally, unless you're expecting to frequently run this code on files which are not actually CNG files, I wouldn't bother with it, since it's easier to just check the validity of the output afterwards. (Besides, the check will fail if the decoded file is actually an Exif JPEG image, which have the magic word "Exif" instead.)

If you do want to include the check, something like this should do it:

local $/ = \(2**16);  # ignore line breaks, read in 64 kiB chunks
while (<>) {
    $_ ^= "\xEF" x length;
    die "Not a valid CNG file" if $. == 1 and not /^.{6}(JFIF|Exif)/s;        
    print;
}

Ps. If this code runs too slow, I'd suggest two possible improvements: 1) use a larger buffer, and b) preallocate the mask of 0xEF bytes instead of rebuilding it on the fly each time:

local $/ = \(2**20);  # ignore line breaks, read in 1 MiB chunks
my $mask = "\xEF" x $$/;
while (<>) {
    $_ ^= substr($mask, 0, length);
    die "Not a valid CNG file" if $. == 1 and not /^.{6}(JFIF|Exif)/s;        
    print;
}

Upvotes: 4

Related Questions