गेम्स से निकाली गई कुछ पीएनजी फाइलें गलत तरीके से क्यों प्रदर्शित होंगी?


14

मैंने कुछ गेम फाइलों से PNG निकालने पर ध्यान दिया है, जिनके माध्यम से छवि विकृत हो जाती है। उदाहरण के लिए, यहाँ Skyrim में बनावट फ़ाइल से निकाले गए कुछ PNG हैं:

Skyrim से प्रकाशित J PNG Skyrim से प्रकाशित K PNG

क्या यह पीएनजी प्रारूप पर कुछ असामान्य बदलाव है? ऐसे PNG को ठीक से देखने के लिए मुझे किन संशोधनों की आवश्यकता होगी?


1
शायद उन्होंने लोगों को इस तरह से सामान करने से रोकने के लिए कुछ विशेष एन्कोडिंग को अपनी फाइलों में डाल दिया है। या हो सकता है कि आप जो भी निकालने के लिए उपयोग कर रहे हैं वह ठीक से काम न करे।
रिचर्ड मार्स्केल -

हो सकता है कि यह फ़ाइलों में छवियों को छोटा करने के लिए एक प्रकार का संपीड़न है। आईफोन ऐप्स में भी ऐसा किया जाता है।
सही

1
विषय से थोड़ा सा, लेकिन क्या यह एक टट्टू है?
जेकोरा

जवाबों:


22

यहाँ "बहाल" छवियां हैं, जो कि टबर्ग के आगे के शोध के लिए धन्यवाद:

final1 final2

जैसा कि अपेक्षित था, 0x4020 बाइट्स के बारे में हर 5-बाइट ब्लॉक मार्कर है । प्रारूप निम्न प्रतीत होता है:

struct marker {
    uint8_t tag;  /* 1 if this is the last marker in the file, 0 otherwise */
    uint16_t len; /* size of the following block (little-endian) */
    uint16_t notlen; /* 0xffff - len */
};

एक बार मार्कर पढ़ने के बाद, अगला marker.lenबाइट्स एक ब्लॉक बनाता है जो फ़ाइल का हिस्सा है। marker.notlenएक नियंत्रण चर ऐसा है कि marker.len + marker.notlen == 0xffff। आखिरी ब्लॉक ऐसा है marker.tag == 1

संरचना शायद इस प्रकार है। अभी भी अज्ञात मूल्य हैं।

struct file {
    uint8_t name_len;    /* number of bytes in the filename */
                         /* (not sure whether it's uint8_t or uint16_t) */
    char name[name_len]; /* filename */
    uint32_t file_len;   /* size of the file (little endian) */
                         /* eg. "40 25 01 00" is 0x12540 bytes */
    uint16_t unknown;    /* maybe a checksum? */

    marker marker1;             /* first block marker (tag == 0) */
    uint8_t data1[marker1.len]; /* data of the first block */
    marker marker2;             /* second block marker (tag == 0) */
    uint8_t data2[marker2.len]; /* data of the second block */
    /* ... */
    marker lastmarker;                /* last block marker (tag == 1) */
    uint8_t lastdata[lastmarker.len]; /* data of the last block */

    uint32_t unknown2; /* end data? another checksum? */
};

मुझे नहीं पता है कि आखिर क्या है, लेकिन चूंकि PNG पैडिंग स्वीकार करते हैं, इसलिए यह बहुत नाटकीय नहीं है। हालाँकि, एन्कोडेड फ़ाइल का आकार स्पष्ट रूप से इंगित करता है कि अंतिम 4 बाइट्स को अनदेखा किया जाना चाहिए ...

चूंकि फ़ाइल की शुरुआत से ठीक पहले मेरे पास सभी ब्लॉक मार्करों तक पहुंच नहीं थी, इसलिए मैंने इस डिकोडर को लिखा जो अंत में शुरू होता है और ब्लॉक मार्करों को खोजने का प्रयास करता है। यह बिल्कुल मजबूत नहीं है, लेकिन अच्छी तरह से, यह आपकी परीक्षण छवियों के लिए काम करता है:

#include <stdio.h>
#include <string.h>

#define MAX_SIZE (1024 * 1024)
unsigned char buf[MAX_SIZE];

/* Usage: program infile.png outfile.png */
int main(int argc, char *argv[])
{
    size_t i, len, lastcheck;
    FILE *f = fopen(argv[1], "rb");
    len = fread(buf, 1, MAX_SIZE, f);
    fclose(f);

    /* Start from the end and check validity */
    lastcheck = len;
    for (i = len - 5; i-- > 0; )
    {
        size_t off = buf[i + 2] * 256 + buf[i + 1];
        size_t notoff = buf[i + 4] * 256 + buf[i + 3];
        if (buf[i] >= 2 || off + notoff != 0xffff)
            continue;
        else if (buf[i] == 1 && lastcheck != len)
            continue;
        else if (buf[i] == 0 && i + off + 5 != lastcheck)
            continue;
        lastcheck = i;
        memmove(buf + i, buf + i + 5, len - i - 5);
        len -= 5;
        i -= 5;
    }

    f = fopen(argv[2], "wb+");
    fwrite(buf, 1, len, f);
    fclose(f);

    return 0;
}

पुराना शोध

0x4022दूसरी छवि से बाइट हटाते समय आपको यही मिलता है , फिर बाइट को हटाकर 0x8092:

मूल पहला कदम दूसरा कदम

यह वास्तव में छवियों की "मरम्मत" नहीं करता है; मैंने यह परीक्षण और त्रुटि के द्वारा किया। हालांकि, यह जो बताता है कि हर 16384 बाइट में अप्रत्याशित डेटा है। मेरा अनुमान है कि छवियों को किसी प्रकार की फाइल सिस्टम संरचना में पैक किया गया है और अप्रत्याशित डेटा केवल ब्लॉक मार्कर हैं जिन्हें आपको डेटा पढ़ते समय निकालना चाहिए।

मुझे नहीं पता कि वास्तव में ब्लॉक मार्कर कहां हैं और उनका आकार, लेकिन ब्लॉक आकार ही सबसे निश्चित रूप से 2 ^ 14 बाइट्स है।

यह मदद करेगा कि क्या आप छवि के ठीक पहले और उसके ठीक बाद दिखाई देने वाली हेक्स डंप (कुछ दर्जन बाइट्स) भी प्रदान कर सकते हैं। यह संकेत देता है कि किस तरह की जानकारी ब्लॉक की शुरुआत या अंत में संग्रहीत है।

बेशक वहाँ भी संभावना है कि आपके निष्कर्षण कोड में एक बग है। यदि आप अपने फ़ाइल संचालन के लिए 16384 बाइट्स के बफर का उपयोग कर रहे हैं, तो मैं सबसे पहले वहां जांच करूंगा।


+1 बहुत सहायक; मैं आपके द्वारा मुझे दी गई लीड के साथ खुदाई करना जारी रखूंगा और कुछ अतिरिक्त जानकारी पोस्ट करूंगा
जेम्स ट्युबर

एम्बेडेड "फाइल" एक लंबाई-उपसर्ग स्ट्रिंग के साथ शुरू होता है जिसमें फ़ाइल नाम होता है; PNG फ़ाइलों के लिए 89 50 4e 47 जादू से पहले 12 बाइट्स के बाद। 12 बाइट्स हैं: 40 25 01 00 78 9c 00 2a 40 d5 bf
जेम्स ट्युबर

अच्छा काम, सैम। मैंने अजगर कोड को अपडेट किया जो वास्तव में बीएसए फ़ाइलों को पढ़ने के लिए सीधे करता है। परिणाम orbza.s3.amazonaws.com/tillberg/pics.html पर दिखाई दे रहे हैं (मैं वहां केवल 1/3 चित्र दिखा रहा हूं, परिणाम प्रदर्शित करने के लिए बस पर्याप्त है)। यह कई छवियों के लिए काम करता है। कुछ अन्य छवियों के साथ कुछ अन्य चीजें चल रही हैं। मैं सोच रहा था कि यह कहीं और हल किया गया है फिर से नतीजा 3 या Skyrim, हालांकि।
tillberg

बहुत बढ़िया काम, दोस्तों! मैं अपने कोड को भी अपडेट करूंगा
जेम्स

18

सैम के सुझाव के आधार पर, मैंने https://github.com/tillberg/skyrim पर जेम्स का कोड फोर्क किया और Skyrim Texts BSA फ़ाइल से n_letter.png को सफलतापूर्वक निकालने में सक्षम था।

अक्षर N

बीएसए हेडर द्वारा दिया गया "file_size" वास्तविक अंतिम फ़ाइल आकार नहीं है। इसमें कुछ हेडर की जानकारी के साथ-साथ बेकार-प्रतीत होने वाले डेटा के कुछ यादृच्छिक भाग शामिल हैं।

हेडर कुछ इस तरह दिखते हैं:

  • 1 बाइट (फ़ाइल पथ की लंबाई?)
  • फ़ाइल का पूर्ण पथ, प्रति वर्ण एक बाइट
  • जेम्स द्वारा पोस्ट किए गए अज्ञात मूल के 12 बाइट्स (40 25 01 00 78 9c 00 2a 40 d5 bf)।

हेडर बाइट्स को हटाने के लिए, मैंने यह किया:

f.seek(file_offset)
data = f.read(file_size)
header_size = 1 + len(folder_path) + len(filename) + 12
d = data[header_size:]

वहाँ से, वास्तविक PNG फ़ाइल शुरू होती है। यह सत्यापित करना आसान है कि PNG 8-बाइट प्रारंभ अनुक्रम से।

मैंने यह पता लगाने की कोशिश की कि जहां पीएनजी हेडर पढ़कर अतिरिक्त बाइट्स स्थित थे और आईडीएटी चंक में पारित लंबाई की तुलना आईआईएडी चंक तक बाइट्स की संख्या को मापने से अनुमान लगाया गया था। (उस पर विवरण के लिए, github पर bsa.py फ़ाइल देखें)

N_letter.png में विखंडू द्वारा दिए गए आकार हैं:

IHDR: 13 bytes
pHYs: 9 bytes
iCCP: 2639 bytes
cHRM: 32 bytes
IDAT: 60625 bytes
IEND: 0 bytes

जब मैंने IDAT चंक और IEND चंक के बीच वास्तविक दूरी को मापा (इसके बाद पायथन में string.find () का उपयोग करके बाइट्स की गिनती करके), तो मैंने पाया कि वास्तविक IDAT लंबाई 60640 बाइट्स थी - वहाँ एक अतिरिक्त 15 बाइट्स थे ।

सामान्य तौर पर, अधिकांश "अक्षर" फाइलों में कुल फ़ाइल आकार के प्रत्येक 16KB के लिए 5 अतिरिक्त बाइट मौजूद होती थीं। उदाहरण के लिए, o_letter.png, लगभग 73KB पर, एक अतिरिक्त 20 बाइट्स था। आर्कन स्क्रिबब्लिंग्स जैसी बड़ी फाइलें, ज्यादातर उसी पैटर्न का अनुसरण करती थीं, हालांकि कुछ में अजीब मात्रा में जोड़े गए (52 बाइट्स, 12 बाइट्स, या 32 बाइट्स)। पता नहीं वहां क्या चल रहा है।

N_letter.png फ़ाइल के लिए, मैं 5-बाइट सेगमेंट को निकालने के लिए सही ऑफ़सेट्स (अधिकतर ट्रायल और एरर) ढूंढने में सक्षम था।

index = 0x403b
index2 = 0x8070
index3 = 0xc0a0
pngdata = (
  d[0      : (index - 5)] + 
  d[index  : (index2 - 5)] + 
  d[index2 : (index3 - 5)] + 
  d[index3 : ] )
pngfile.write(pngdata)

निकाले गए पांच बाइट खंड हैं:

at 000000: 00 2A 40 D5 BF (<-- included at end of 12 bytes above)
at 00403B: 00 30 40 CF BF
at 008070: 00 2B 40 D4 BF
at 00C0A0: 01 15 37 EA C8

इसके लायक क्या है, मैंने अज्ञात 12-बाइट खंड के अंतिम पांच बाइट्स को शामिल किया है क्योंकि अन्य अनुक्रमों के साथ कुछ समानता है।

यह पता चला है कि वे हर 16KB नहीं हैं, लेकिन ~ 0x4030 बाइट अंतराल पर।

उपरोक्त सूचकांकों में करीबी-लेकिन-नहीं-सही मैच प्राप्त करने के लिए गार्ड करने के लिए, मैंने परिणामस्वरूप पीएनजी से आईडीएटी चंक के ज़ालिब विघटन का भी परीक्षण किया, और यह गुजरता है।


"एक यादृच्छिक @ चिह्न के लिए 1 बाइट" फ़ाइल नाम स्ट्रिंग की लंबाई है, मेरा मानना ​​है
जेम्स ट्युबर

प्रत्येक मामले में 5-बाइट सेगमेंट का मूल्य क्या है?
जेम्स

मैंने अपने उत्तर को हटाए गए 5-बाइट खंडों के हेक्स मानों के साथ अद्यतन किया। इसके अलावा, मैंने खुद को 5-बाइट सेगमेंट की संख्या में मिलाया था (मैं पहले रहस्यमय 12-बाइट हेडर की गिनती 7 बाइट्स हेडर और 5 बाइट्स रिपीट डिवाइडर के रूप में कर रहा था)। मैंने यह भी तय किया।
tillberg

ध्यान दें कि (छोटे-एंडियन) 0x402A, 0x4030, 0x402B उन 5-बाइट सेगमेंट में दिखाई देते हैं; क्या वे वास्तविक अंतराल हैं?
जेम्स टाउबर

मुझे लगा कि मैंने पहले ही कहा था कि यह उत्कृष्ट काम था, लेकिन जाहिर तौर पर मैंने ऐसा नहीं किया। उत्कृष्ट कार्य! :-)
सैम होसेवर

3

दरअसल, आंतरायिक 5 बाइट्स ज़ालिब संपीड़न का हिस्सा हैं।

Http://drj11.wordpress.com/2007/11/20/a-use-for-uncompressed-panss/ पर विस्तृत रूप से ,

01 छोटे एंडियन बिट स्ट्रिंग 1 00 00000। 1 अंतिम ब्लॉक को दर्शाता है, 00 एक गैर-संपीड़ित ब्लॉक का संकेत देता है, और 00000 5 बिट्स हैं जो ब्लॉक को ऑक्टेट पर ब्लॉक शुरू करने के लिए संरेखित करता है (जो गैर-संकुचित ब्लॉकों के लिए आवश्यक है। , और मेरे लिए बहुत सुविधाजनक है)। 05 00 fa ff असम्पीडित ब्लॉक में डेटा के ओकटेट की संख्या (5)। एक छोटे से 16-बिट पूर्णांक के रूप में संग्रहीत इसके 1 complement पूरक (!) द्वारा पीछा किया गया।

.. तो एक 00 एक 'अगला' ब्लॉक (एक को समाप्त करने वाला नहीं) इंगित करता है, और 4 अगले बाइट्स ब्लॉक की लंबाई और इसके व्युत्क्रम हैं।

[संपादित करें] एक अधिक विश्वसनीय स्रोत निश्चित रूप से RFC 1951 (संपीडित संपीडित डेटा प्रारूप विशिष्टता), खंड 3.2.4 है।


1

क्या यह संभव है कि आप किसी बाइनरी मोड के बजाय फाइल से डेटा को टेक्स्ट मोड (जहां पीएनजी डेटा में दिखाई देने वाली लाइन एंडिंग संभवत: मंगाई गई हो) से पढ़ रहे हों?


1
ऐ। यह मुद्दा बहुत पसंद है। इसे ध्यान में रखते हुए कोड को पढ़ता है: github.com/jtauber/skyrim/blob/master/bsa.py --- पुष्टि :-)
Armin Ronacher

नहीं, कोई फर्क नहीं पड़ता।
जेम्स

@JamesTauber, यदि आप वास्तव में अपने खुद के PNG लोडर को कोड कर रहे हैं जैसा कि Armin की टिप्पणी से लगता है, तो (a) क्या यह आपके द्वारा आजमाए गए अन्य PNG पर काम करता है, और (b) एक सिद्ध PNG लोडर जैसे libpngकि Skyrim PNGs को पढ़ता है? दूसरे शब्दों में, क्या यह आपके पीएनजी लोडर में सिर्फ एक बग है?
नाथन रीड

@NathanReed सभी मैं कर रहा हूँ बाइट स्ट्रीम निकाल रहा है और इसे यहाँ अपलोड कर रहा है; इसमें कोई "लोडर" शामिल नहीं है
जेम्स ट्युबर

3
-1, यह कारण नहीं हो सकता। यदि इस तरह से PNG फाइलें दूषित हो जाती हैं, तो छवि कोडिंग चरण में त्रुटियों से पहले फुलाया हुआ चरण में CRC त्रुटियां होंगी। इसके अलावा, हेडर में अपेक्षित एक के अलावा फाइलों में CRLF की कोई घटना नहीं है।
sam hocevar
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.