Ticket #3815: partial_fix_for_bug_2019355-v1.patch

File partial_fix_for_bug_2019355-v1.patch, 19.9 KB (added by SF/buddha_, 16 years ago)

Partial fix for the bug (1st version). Uses Unix style newlines. At least fw-amiga-it.1 of the old savegame set still bugs.

  • engines/cine/script_fw.cpp

     
    230230 * \param fHandle Savefile open for reading
    231231 * \param len Size of array
    232232 */
    233 ScriptVars::ScriptVars(Common::InSaveFile &fHandle, unsigned int len)
     233ScriptVars::ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len)
    234234        : _size(len), _vars(new int16[len]) {
    235235
    236236        assert(_vars);
     
    306306/*! \brief Restore array from savefile
    307307 * \param fHandle Savefile open for reading
    308308 */
    309 void ScriptVars::load(Common::InSaveFile &fHandle) {
     309void ScriptVars::load(Common::SeekableReadStream &fHandle) {
    310310        load(fHandle, _size);
    311311}
    312312
     
    314314 * \param fHandle Savefile open for reading
    315315 * \param len Length of data to be read
    316316 */
    317 void ScriptVars::load(Common::InSaveFile &fHandle, unsigned int len) {
     317void ScriptVars::load(Common::SeekableReadStream &fHandle, unsigned int len) {
    318318        debug(6, "assert(%d <= %d)", len, _size);
    319319        assert(len <= _size);
    320320        for (unsigned int i = 0; i < len; i++) {
  • engines/cine/script.h

     
    6161public:
    6262        // Explicit to prevent var=0 instead of var[i]=0 typos.
    6363        explicit ScriptVars(unsigned int len = 50);
    64         ScriptVars(Common::InSaveFile &fHandle, unsigned int len = 50);
     64        ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len = 50);
    6565        ScriptVars(const ScriptVars &src);
    6666        ~ScriptVars(void);
    6767
     
    7171
    7272        void save(Common::OutSaveFile &fHandle) const;
    7373        void save(Common::OutSaveFile &fHandle, unsigned int len) const;
    74         void load(Common::InSaveFile &fHandle);
    75         void load(Common::InSaveFile &fHandle, unsigned int len);
     74        void load(Common::SeekableReadStream &fHandle);
     75        void load(Common::SeekableReadStream &fHandle, unsigned int len);
    7676        void reset(void);
    7777};
    7878
  • engines/cine/bg_list.cpp

     
    8383/*! \brief Restore incrust list from savefile
    8484 * \param fHandle Savefile open for reading
    8585 */
    86 void loadBgIncrustFromSave(Common::InSaveFile &fHandle) {
     86void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle) {
    8787        BGIncrust tmp;
    8888        int size = fHandle.readSint16BE();
    8989
  • engines/cine/anim.cpp

     
    769769
    770770/*! \brief Load animDataTable from save
    771771 * \param fHandle Savefile open for reading
    772  * \param broken Broken/correct file format switch
     772 * \param saveGameFormat The used savegame format
    773773 * \todo Add Operation Stealth savefile support
    774774 *
    775775 * Unlike the old code, this one actually rebuilds the table one frame
    776776 * at a time.
    777777 */
    778 void loadResourcesFromSave(Common::InSaveFile &fHandle, bool broken) {
     778void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) {
    779779        int16 currentAnim, foundFileIdx;
    780780        int8 isMask = 0, isSpl = 0;
    781781        byte *dataPtr, *ptr;
     
    791791
    792792        strcpy(part, currentPartName);
    793793
     794        // We only support these variations of the savegame format at the moment.
     795        assert(saveGameFormat == ANIMSIZE_23 || saveGameFormat == ANIMSIZE_30_PTRS_INTACT);
     796
    794797        for (currentAnim = 0; currentAnim < NUM_MAX_ANIMDATA; currentAnim++) {
    795798                currentPtr = &animDataTable[currentAnim];
    796799
     
    799802                bpp = fHandle.readUint16BE();
    800803                height = fHandle.readUint16BE();
    801804
    802                 if (!broken) {
    803                         if (!fHandle.readUint32BE()) {
    804                                 fHandle.skip(18);
    805                                 continue;
    806                         }
    807                         fHandle.readUint32BE();
     805                bool validPtr = false;
     806                if (saveGameFormat == ANIMSIZE_30_PTRS_INTACT) {                       
     807                        validPtr = (fHandle.readUint32BE() != 0); // Read data pointer
     808                        fHandle.readUint32BE(); // Discard mask pointer
    808809                }
    809810
    810811                foundFileIdx = fHandle.readSint16BE();
    811812                frame = fHandle.readSint16BE();
    812813                fHandle.read(name, 10);
    813814
    814                 if (foundFileIdx < 0 || (broken && !fHandle.readByte())) {
     815                if (saveGameFormat == ANIMSIZE_23) {
     816                        validPtr = (fHandle.readByte() != 0);
     817                }
     818
     819                // Don't try to load invalid entries.
     820                if (foundFileIdx < 0 || !validPtr) {
    815821                        continue;
    816822                }
    817823
  • engines/cine/various.cpp

     
    246246        return true;
    247247}
    248248
     249/*! \brief Savegame format detector
     250 * \param fHandle Savefile to check
     251 * \return Savegame format on success, ANIMSIZE_UNKNOWN on failure
     252 *
     253 * This function seek through the savefile and tries to determine the
     254 * savegame format it uses. There's a miniscule chance that the detection
     255 * algorithm could get confused and think that the file uses both the older
     256 * and the newer format but that is such a remote possibility that I wouldn't
     257 * worry about it at all.
     258 */
     259enum CineSaveGameFormat detectSaveGameFormat(Common::SeekableReadStream &fHandle) {
     260        // The animDataTable begins at savefile position 0x2315.
     261        // Each animDataTable entry takes 23 bytes in older saves (Revisions 21772-31443)
     262        // and 30 bytes in the save format after that (Revision 31444 and onwards).
     263        // There are 255 entries in the animDataTable in both of the savefile formats.
     264        static const uint animDataTableStart = 0x2315;
     265        static const uint entriesCount = 255;
     266        static const uint oldEntrySize = 23;
     267        static const uint newEntrySize = 30;
     268        static const uint defaultEntrySize = newEntrySize;
     269        static const uint entrySizeChoices[] = {oldEntrySize, newEntrySize};
     270        Common::Array<uint> entrySizeMatches;
     271        const uint32 prevStreamPos = fHandle.pos();
     272
     273        // Try to walk through the savefile using different animDataTable entry sizes
     274        // and make a list of all the successful entry sizes.
     275        for (uint i = 0; i < ARRAYSIZE(entrySizeChoices); i++) {
     276                // 206 = 2 * 50 * 2 + 2 * 3 (Size of global and object script entries)
     277                // 20 = 4 * 2 + 2 * 6 (Size of overlay and background incrust entries)
     278                static const uint sizeofScreenParams = 2 * 6;
     279                static const uint globalScriptEntrySize = 206;
     280                static const uint objectScriptEntrySize = 206;
     281                static const uint overlayEntrySize = 20;
     282                static const uint bgIncrustEntrySize = 20;
     283                static const uint entrySizes[] = {
     284                        globalScriptEntrySize,
     285                        objectScriptEntrySize,
     286                        overlayEntrySize,
     287                        bgIncrustEntrySize
     288                };
     289               
     290                uint entrySize = entrySizeChoices[i];           
     291                // Jump over the animDataTable entries and the screen parameters
     292                uint32 newPos = animDataTableStart + entrySize * entriesCount + sizeofScreenParams;
     293                // Check that there's data left after the point we're going to jump to
     294                if (newPos >= fHandle.size()) {
     295                        continue;
     296                }
     297                fHandle.seek(newPos);
     298
     299                // Jump over the remaining items in the savegame file
     300                bool chainWalkSuccess = true;
     301                for (uint j = 0; j < ARRAYSIZE(entrySizes); j++) {
     302                        // Read entry count and jump over the entries
     303                        int entryCount = fHandle.readSint16BE();
     304                        newPos = fHandle.pos() + entrySizes[j] * entryCount;
     305                        // Check that we didn't go past the end of file.
     306                        // Note that getting exactly to the end of file is acceptable.
     307                        if (newPos > fHandle.size()) {
     308                                chainWalkSuccess = false;
     309                                break;
     310                        }
     311                        fHandle.seek(newPos);
     312                }
     313               
     314                // If we could walk the chain successfully and
     315                // got exactly to the end of file then we've got a match.
     316                if (chainWalkSuccess && fHandle.pos() == fHandle.size()) {
     317                        // We found a match, let's save it
     318                        entrySizeMatches.push_back(entrySize);
     319                }
     320        }
     321
     322        // Check that we got only one entry size match.
     323        // If we didn't, then return an error.
     324        enum CineSaveGameFormat result = ANIMSIZE_UNKNOWN;
     325        if (entrySizeMatches.size() == 1) {
     326                const uint entrySize = entrySizeMatches[0];
     327                assert(entrySize == oldEntrySize || entrySize == newEntrySize);
     328                if (entrySize == oldEntrySize) {
     329                        result = ANIMSIZE_23;
     330                } else { // entrySize == newEntrySize           
     331                        // Check data and mask pointers in all of the animDataTable entries
     332                        // to see whether we've got the version with the broken data and mask pointers or not.
     333                        // In the broken format all data and mask pointers were always zero.
     334                        static const uint relativeDataPos = 2 * 4;
     335                        bool pointersIntact = false;
     336                        for (uint i = 0; i < entriesCount; i++) {
     337                                fHandle.seek(animDataTableStart + i * entrySize + relativeDataPos);
     338                                uint32 data = fHandle.readUint32BE();
     339                                uint32 mask = fHandle.readUint32BE();
     340                                if (data != NULL || mask != NULL) {
     341                                        pointersIntact = true;
     342                                        break;
     343                                }
     344                        }
     345                        result = (pointersIntact ? ANIMSIZE_30_PTRS_INTACT : ANIMSIZE_30_PTRS_BROKEN);
     346                }
     347        } else if (entrySizeMatches.size() > 1) {
     348                warning("Savegame format detector got confused by input data. Detecting savegame to be using an unknown format");
     349        } else { // entrySizeMatches.size() == 0
     350                debug(3, "Savegame format detector was unable to detect savegame's format");
     351        }
     352
     353        fHandle.seek(prevStreamPos);
     354        return result;
     355}
     356
    249357/*! \brief Restore script list item from savefile
    250  * \param fHandle Savefile handlem open for reading
     358 * \param fHandle Savefile handle open for reading
    251359 * \param isGlobal Restore object or global script?
    252360 */
    253 void loadScriptFromSave(Common::InSaveFile *fHandle, bool isGlobal) {
     361void loadScriptFromSave(Common::SeekableReadStream &fHandle, bool isGlobal) {
    254362        ScriptVars localVars, labels;
    255363        uint16 compare, pos;
    256364        int16 idx;
    257365
    258         labels.load(*fHandle);
    259         localVars.load(*fHandle);
     366        labels.load(fHandle);
     367        localVars.load(fHandle);
    260368
    261         compare = fHandle->readUint16BE();
    262         pos = fHandle->readUint16BE();
    263         idx = fHandle->readUint16BE();
     369        compare = fHandle.readUint16BE();
     370        pos = fHandle.readUint16BE();
     371        idx = fHandle.readUint16BE();
    264372
    265373        // no way to reinitialize these
    266374        if (idx < 0) {
     
    283391/*! \brief Restore overlay sprites from savefile
    284392 * \param fHandle Savefile open for reading
    285393 */
    286 void loadOverlayFromSave(Common::InSaveFile &fHandle) {
     394void loadOverlayFromSave(Common::SeekableReadStream &fHandle) {
    287395        overlay tmp;
    288396
    289397        fHandle.readUint32BE();
     
    299407        overlayList.push_back(tmp);
    300408}
    301409
    302 /*! \brief Savefile format tester
    303  * \param fHandle Savefile to check
    304  *
    305  * This function seeks through savefile and tries to guess if it's the original
    306  * savegame format or broken format from ScummVM 0.10/0.11
    307  * The test is incomplete but this should cover 99.99% of cases.
    308  * If anyone makes a savefile which could confuse this test, assert will
    309  * report it
    310  */
    311 bool brokenSave(Common::InSaveFile &fHandle) {
    312         // Backward seeking not supported in compressed savefiles
    313         // if you really want it, finish it yourself
    314         return false;
    315 
    316         // fixed size part: 14093 bytes (12308 bytes in broken save)
    317         // animDataTable begins at byte 6431
    318 
    319         int filesize = fHandle.size();
    320         int startpos = fHandle.pos();
    321         int pos, tmp;
    322         bool correct = false, broken = false;
    323 
    324         // check for correct format
    325         while (filesize > 14093) {
    326                 pos = 14093;
    327 
    328                 fHandle.seek(pos);
    329                 tmp = fHandle.readUint16BE();
    330                 pos += 2 + tmp * 206;
    331                 if (pos >= filesize) break;
    332 
    333                 fHandle.seek(pos);
    334                 tmp = fHandle.readUint16BE();
    335                 pos += 2 + tmp * 206;
    336                 if (pos >= filesize) break;
    337 
    338                 fHandle.seek(pos);
    339                 tmp = fHandle.readUint16BE();
    340                 pos += 2 + tmp * 20;
    341                 if (pos >= filesize) break;
    342 
    343                 fHandle.seek(pos);
    344                 tmp = fHandle.readUint16BE();
    345                 pos += 2 + tmp * 20;
    346 
    347                 if (pos == filesize) correct = true;
    348                 break;
    349         }
    350         debug(5, "brokenSave: correct format check %s: size=%d, pos=%d",
    351                 correct ? "passed" : "failed", filesize, pos);
    352 
    353         // check for broken format
    354         while (filesize > 12308) {
    355                 pos = 12308;
    356 
    357                 fHandle.seek(pos);
    358                 tmp = fHandle.readUint16BE();
    359                 pos += 2 + tmp * 206;
    360                 if (pos >= filesize) break;
    361 
    362                 fHandle.seek(pos);
    363                 tmp = fHandle.readUint16BE();
    364                 pos += 2 + tmp * 206;
    365                 if (pos >= filesize) break;
    366 
    367                 fHandle.seek(pos);
    368                 tmp = fHandle.readUint16BE();
    369                 pos += 2 + tmp * 20;
    370                 if (pos >= filesize) break;
    371 
    372                 fHandle.seek(pos);
    373                 tmp = fHandle.readUint16BE();
    374                 pos += 2 + tmp * 20;
    375 
    376                 if (pos == filesize) broken = true;
    377                 break;
    378         }
    379         debug(5, "brokenSave: broken format check %s: size=%d, pos=%d",
    380                 broken ? "passed" : "failed", filesize, pos);
    381 
    382         // there's a very small chance that both cases will match
    383         // if anyone runs into it, you'll have to walk through
    384         // the animDataTable and try to open part file for each entry
    385         if (!correct && !broken) {
    386                 error("brokenSave: file format check failed");
    387         } else if (correct && broken) {
    388                 error("brokenSave: both file formats seem to apply");
    389         }
    390 
    391         fHandle.seek(startpos);
    392         debug(5, "brokenSave: detected %s file format",
    393                 correct ? "correct" : "broken");
    394 
    395         return broken;
    396 }
    397 
    398410/*! \todo Implement Operation Stealth loading, this is obviously Future Wars only
    399411 * \todo Add support for loading the zoneQuery table (Operation Stealth specific)
    400412 */
    401413bool CineEngine::makeLoad(char *saveName) {
    402414        int16 i;
    403415        int16 size;
    404         bool broken;
    405         Common::InSaveFile *fHandle;
    406416        char bgName[13];
    407417
    408         fHandle = g_saveFileMan->openForLoading(saveName);
     418        Common::SharedPtr<Common::InSaveFile> saveFile(g_saveFileMan->openForLoading(saveName));
    409419
    410         if (!fHandle) {
     420        if (!saveFile) {
    411421                drawString(otherMessages[0], 0);
    412422                waitPlayerInput();
    413423                // restoreScreen();
     
    415425                return false;
    416426        }
    417427
     428        uint32 saveSize = saveFile->size();
     429        if (saveSize == 0) { // Savefile's compressed using zlib format can't tell their unpacked size, test for it
     430                // Can't get information about the savefile's size so let's try
     431                // reading as much as we can from the file up to a predefined upper limit.
     432                //
     433                // Some estimates for maximum savefile sizes (All with 255 animDataTable entries of 30 bytes each):
     434                // With 256 global scripts, object scripts, overlays and background incrusts:
     435                // 0x2315 + (255 * 30) + (2 * 6) + (206 + 206 + 20 + 20) * 256 = ~129kB
     436                // With 512 global scripts, object scripts, overlays and background incrusts:
     437                // 0x2315 + (255 * 30) + (2 * 6) + (206 + 206 + 20 + 20) * 512 = ~242kB
     438                //
     439                // I think it extremely unlikely that there would be over 512 global scripts, object scripts,
     440                // overlays and background incrusts so 256kB seems like quite a safe upper limit.               
     441                // NOTE: If the savegame format is changed then this value might have to be re-evaluated!
     442                // Hopefully devices with more limited memory can also cope with this memory allocation.
     443                saveSize = 256 * 1024;
     444        }
     445        Common::SharedPtr<Common::MemoryReadStream> fHandle(saveFile->readStream(saveSize));
     446
     447        // Try to detect the used savegame format
     448        enum CineSaveGameFormat saveGameFormat = detectSaveGameFormat(*fHandle);
     449
     450        // Handle problematic savegame formats
     451        if (saveGameFormat == ANIMSIZE_30_PTRS_BROKEN) {
     452                // One might be able to load the ANIMSIZE_30_PTRS_BROKEN format but
     453                // that's not implemented here because it was never used in a stable
     454                // release of ScummVM but only during development (From revision 31453,
     455                // which introduced the problem, until revision 32073, which fixed it).
     456                // Therefore be bail out if we detect this particular savegame format.
     457                warning("Detected a known broken savegame format, not loading savegame");
     458                return false;
     459        } else if (saveGameFormat == ANIMSIZE_UNKNOWN) {
     460                // If we can't detect the savegame format
     461                // then let's try the default format and hope for the best.
     462                warning("Couldn't detect the used savegame format, trying default savegame format. Things may break");
     463                saveGameFormat = ANIMSIZE_30_PTRS_INTACT;
     464        }
     465        // Now we should have either of these formats
     466        assert(saveGameFormat == ANIMSIZE_23 || saveGameFormat == ANIMSIZE_30_PTRS_INTACT);
     467
    418468        g_sound->stopMusic();
    419469        freeAnimDataTable();
    420470        overlayList.clear();
     
    464514
    465515        checkForPendingDataLoadSwitch = 0;
    466516
    467         broken = brokenSave(*fHandle);
    468517
    469518        // At savefile position 0x0000:
    470519        currentDisk = fHandle->readUint16BE();
     
    588637        fHandle->readUint16BE();
    589638
    590639        // At 0x2315:
    591         loadResourcesFromSave(*fHandle, broken);
     640        loadResourcesFromSave(*fHandle, saveGameFormat);
    592641
    593642        // TODO: handle screen params (really required ?)
    594643        fHandle->readUint16BE();
     
    600649
    601650        size = fHandle->readSint16BE();
    602651        for (i = 0; i < size; i++) {
    603                 loadScriptFromSave(fHandle, true);
     652                loadScriptFromSave(*fHandle, true);
    604653        }
    605654
    606655        size = fHandle->readSint16BE();
    607656        for (i = 0; i < size; i++) {
    608                 loadScriptFromSave(fHandle, false);
     657                loadScriptFromSave(*fHandle, false);
    609658        }
    610659
    611660        size = fHandle->readSint16BE();
     
    615664
    616665        loadBgIncrustFromSave(*fHandle);
    617666
    618         delete fHandle;
    619 
    620667        if (strlen(currentMsgName)) {
    621668                loadMsg(currentMsgName);
    622669        }
  • engines/cine/gfx.h

     
    113113
    114114        virtual void refreshPalette();
    115115        virtual void reloadPalette();
    116         void restorePalette(Common::InSaveFile &fHandle);
     116        void restorePalette(Common::SeekableReadStream &fHandle);
    117117        void savePalette(Common::OutSaveFile &fHandle);
    118118        virtual void rotatePalette(int a, int b, int c);
    119119        virtual void transformPalette(int first, int last, int r, int g, int b);
  • engines/cine/gfx.cpp

     
    614614/*! \brief Restore active and backup palette from save
    615615 * \param fHandle Savefile open for reading
    616616 */
    617 void FWRenderer::restorePalette(Common::InSaveFile &fHandle) {
     617void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
    618618        int i;
    619619
    620620        if (!_palette) {
  • engines/cine/bg_list.h

     
    5151
    5252void createBgIncrustListElement(int16 objIdx, int16 param);
    5353void resetBgIncrustList(void);
    54 void loadBgIncrustFromSave(Common::InSaveFile &fHandle);
     54void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle);
    5555
    5656} // End of namespace Cine
    5757
  • engines/cine/anim.h

     
    101101void freeAnimDataRange(byte startIdx, byte numIdx);
    102102void loadResource(const char *resourceName);
    103103void loadAbs(const char *resourceName, uint16 idx);
    104 void loadResourcesFromSave(Common::InSaveFile &fHandle, bool broken);
     104void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat);
    105105void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency);
    106106
    107107} // End of namespace Cine
  • engines/cine/various.h

     
    3333
    3434namespace Cine {
    3535
     36/**
     37 * Cine engine's save game versions.
     38 * Enumeration entries (Excluding the one used as an error)
     39 * are sorted according to age (i.e. top one is oldest, last one newest etc).
     40 *
     41 * ANIMSIZE_UNKNOWN:
     42 * - Animation data entry size is unknown (Used as an error).
     43 *
     44 * ANIMSIZE_23:
     45 * - Animation data entry size size is 23 bytes.
     46 * - Used for an example by the 0.11.1 release of ScummVM.
     47 *
     48 * ANIMSIZE_30_PTRS_BROKEN:
     49 * - Animation data entry size size is 30 bytes.
     50 * - Data and mask pointers in the saved structs are always NULL.
     51 *
     52 * ANIMSIZE_30_PTRS_INTACT:
     53 * - Animation data entry size size is 30 bytes.
     54 * - Data and mask pointers in the saved structs are intact,
     55 *   so you can test them for equality or inequality with NULL
     56 *   but don't try using them for anything else, it won't work.
     57 */
     58enum CineSaveGameFormat {
     59        ANIMSIZE_UNKNOWN,
     60        ANIMSIZE_23,
     61        ANIMSIZE_30_PTRS_BROKEN,
     62        ANIMSIZE_30_PTRS_INTACT
     63};
     64
    3665void initLanguage(Common::Language lang);
    3766
    3867int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, uint16 Y, uint16 width, bool recheckValue = false);