NScumm.Scumm.ScummEngine.SaveOrLoad C# (CSharp) Method

SaveOrLoad() protected method

protected SaveOrLoad ( Serializer serializer ) : void
serializer NScumm.Scumm.IO.Serializer
return void
        protected virtual void SaveOrLoad(Serializer serializer)
        {
            uint ENCD_offs = 0;
            uint EXCD_offs = 0;
            uint IM00_offs = 0;
            uint CLUT_offs = 0;
            uint EPAL_offs = 0;
            uint PALS_offs = 0;
            byte numObjectsInRoom = (byte)_objs.Length;

            #region MainEntries

            var mainEntries = new[]
            {
                LoadAndSaveEntry.Create(reader => _gameMD5 = reader.ReadBytes(16), writer => writer.Write(_gameMD5), 39),
                LoadAndSaveEntry.Create(reader => reader.ReadUInt16(), writer => writer.Write(roomData.Header.Width), 8, 50),
                LoadAndSaveEntry.Create(reader => reader.ReadUInt16(), writer => writer.Write(roomData.Header.Height), 8, 50),
                LoadAndSaveEntry.Create(reader => ENCD_offs = reader.ReadUInt32(), writer => writer.Write(ENCD_offs), 8, 50),
                LoadAndSaveEntry.Create(reader => EXCD_offs = reader.ReadUInt32(), writer => writer.Write(EXCD_offs), 8, 50),
                LoadAndSaveEntry.Create(reader => IM00_offs = reader.ReadUInt32(), writer => writer.Write(IM00_offs), 8, 50),
                LoadAndSaveEntry.Create(reader => CLUT_offs = reader.ReadUInt32(), writer => writer.Write(CLUT_offs), 8, 50),
                LoadAndSaveEntry.Create(reader => EPAL_offs = reader.ReadUInt32(), writer => writer.Write(EPAL_offs), 8, 9),
                LoadAndSaveEntry.Create(reader => PALS_offs = reader.ReadUInt32(), writer => writer.Write(PALS_offs), 8, 50),
                LoadAndSaveEntry.Create(reader => _curPalIndex = reader.ReadByte(), writer => writer.WriteByte(_curPalIndex), 8),
                LoadAndSaveEntry.Create(reader => _currentRoom = reader.ReadByte(), writer => writer.Write(_currentRoom), 8),
                LoadAndSaveEntry.Create(reader => _roomResource = reader.ReadByte(), writer => writer.Write(_roomResource), 8),
                LoadAndSaveEntry.Create(reader => numObjectsInRoom = reader.ReadByte(), writer => writer.Write(numObjectsInRoom), 8),
                LoadAndSaveEntry.Create(reader => CurrentScript = reader.ReadByte(), writer => writer.Write(CurrentScript), 8),
                LoadAndSaveEntry.Create(reader => reader.ReadUInt32s(NumLocalScripts), writer => writer.Write(new uint[NumLocalScripts], NumLocalScripts), 8, 50),
                // vm.localvar grew from 25 to 40 script entries and then from
                // 16 to 32 bit variables (but that wasn't reflect here)... and
                // THEN from 16 to 25 variables.
                LoadAndSaveEntry.Create(reader =>
                    {
                        for (int i = 0; i < 25; i++)
                        {
                            _slots[i].InitializeLocals(reader.ReadUInt16s(17));
                        }
                    }, writer =>
                    {
                        for (int i = 0; i < 25; i++)
                        {
                            writer.WriteUInt16s(_slots[i].LocalVariables.Cast<ushort>().ToArray(), 17);
                        }
                    }, 8, 8),
                LoadAndSaveEntry.Create(reader =>
                    {
                        for (int i = 0; i < 40; i++)
                        {
                            _slots[i].InitializeLocals(reader.ReadUInt16s(17));
                        }
                    }, writer =>
                    {
                        for (int i = 0; i < 40; i++)
                        {
                            writer.WriteUInt16s(_slots[i].LocalVariables.Cast<ushort>().ToArray(), 17);
                        }
                    }, 9, 14),
                // We used to save 25 * 40 = 1000 blocks; but actually, each 'row consisted of 26 entry,
                // i.e. 26 * 40 = 1040. Thus the last 40 blocks of localvar where not saved at all. To be
                // able to load this screwed format, we use a trick: We load 26 * 38 = 988 blocks.
                // Then, we mark the followin 12 blocks (24 bytes) as obsolete.
                LoadAndSaveEntry.Create(reader =>
                    {
                        for (int i = 0; i < 38; i++)
                        {
                            _slots[i].InitializeLocals(reader.ReadUInt16s(26));
                        }
                    }, writer =>
                    {
                        for (int i = 0; i < 38; i++)
                        {
                            writer.WriteUInt16s(_slots[i].LocalVariables.Cast<ushort>().ToArray(), 26);
                        }
                    }, 15, 17),
                // TODO
                //MK_OBSOLETE_ARRAY(ScummEngine, vm.localvar[39][0], sleUint16, 12, VER(15), VER(17)),
                // This was the first proper multi dimensional version of the localvars, with 32 bit values
                LoadAndSaveEntry.Create(reader =>
                    {
                        for (int i = 0; i < 40; i++)
                        {
                            _slots[i].InitializeLocals(reader.ReadInt32s(26));
                        }
                    }, writer =>
                    {
                        for (int i = 0; i < 40; i++)
                        {
                            writer.WriteInt32s(_slots[i].LocalVariables, 26);
                        }
                    }, 18, 19),

                // Then we doubled the script slots again, from 40 to 80
                LoadAndSaveEntry.Create(reader =>
                    {
                        for (int i = 0; i < NumScriptSlot; i++)
                        {
                            _slots[i].InitializeLocals(reader.ReadInt32s(26));
                        }
                    }, writer =>
                    {
                        for (int i = 0; i < NumScriptSlot; i++)
                        {
                            writer.WriteInt32s(_slots[i].LocalVariables, 26);
                        }
                    }, 20),

                LoadAndSaveEntry.Create(reader => _resourceMapper = reader.ReadBytes(128), writer => writer.Write(_resourceMapper), 8),
                LoadAndSaveEntry.Create(reader => CharsetColorMap = reader.ReadBytes(16), writer => writer.Write(CharsetColorMap), 8),

                // _charsetData grew from 10*16, to 15*16, to 23*16 bytes
                LoadAndSaveEntry.Create(reader => reader.ReadMatrixBytes(10, 16), writer => writer.WriteMatrixBytes(new byte[16, 10], 10, 16), 8, 9),
                LoadAndSaveEntry.Create(reader => reader.ReadMatrixBytes(15, 16), writer => writer.WriteMatrixBytes(new byte[16, 15], 15, 16), 10, 66),
                LoadAndSaveEntry.Create(reader => reader.ReadMatrixBytes(23, 16), writer => writer.WriteMatrixBytes(new byte[16, 23], 23, 16), 67),

                LoadAndSaveEntry.Create(reader => reader.ReadUInt16(), writer => writer.WriteUInt16(0), 8, 62),

                LoadAndSaveEntry.Create(reader => _camera.DestinationPosition.X = reader.ReadInt16(), writer => writer.WriteInt16(_camera.DestinationPosition.X), 8),
                LoadAndSaveEntry.Create(reader => _camera.DestinationPosition.Y = reader.ReadInt16(), writer => writer.WriteInt16(_camera.DestinationPosition.Y), 8),
                LoadAndSaveEntry.Create(reader => _camera.CurrentPosition.X = reader.ReadInt16(), writer => writer.WriteInt16(_camera.CurrentPosition.X), 8),
                LoadAndSaveEntry.Create(reader => _camera.CurrentPosition.Y = reader.ReadInt16(), writer => writer.WriteInt16(_camera.CurrentPosition.Y), 8),
                LoadAndSaveEntry.Create(reader => _camera.LastPosition.X = reader.ReadInt16(), writer => writer.WriteInt16(_camera.LastPosition.X), 8),
                LoadAndSaveEntry.Create(reader => _camera.LastPosition.Y = reader.ReadInt16(), writer => writer.WriteInt16(_camera.LastPosition.Y), 8),
                LoadAndSaveEntry.Create(reader => _camera.Accel.X = reader.ReadInt16(), writer => writer.WriteInt16(_camera.Accel.X), 8),
                LoadAndSaveEntry.Create(reader => _camera.Accel.Y = reader.ReadInt16(), writer => writer.WriteInt16(_camera.Accel.Y), 8),
                LoadAndSaveEntry.Create(reader => _screenStartStrip = reader.ReadInt16(), writer => writer.WriteInt16(_screenStartStrip), 8),
                LoadAndSaveEntry.Create(reader => _screenEndStrip = reader.ReadInt16(), writer => writer.WriteInt16(_screenEndStrip), 8),
                LoadAndSaveEntry.Create(reader => _camera.Mode = (CameraMode)reader.ReadByte(), writer => writer.Write((byte)_camera.Mode), 8),
                LoadAndSaveEntry.Create(reader => _camera.ActorToFollow = reader.ReadByte(), writer => writer.Write(_camera.ActorToFollow), 8),
                LoadAndSaveEntry.Create(reader => _camera.LeftTrigger = reader.ReadInt16(), writer => writer.WriteInt16(_camera.LeftTrigger), 8),
                LoadAndSaveEntry.Create(reader => _camera.RightTrigger = reader.ReadInt16(), writer => writer.WriteInt16(_camera.RightTrigger), 8),
                LoadAndSaveEntry.Create(reader => _camera.MovingToActor = reader.ReadUInt16() != 0, writer => writer.WriteUInt16(_camera.MovingToActor), 8),

                LoadAndSaveEntry.Create(reader => _actorToPrintStrFor = reader.ReadByte(), writer => writer.WriteByte(_actorToPrintStrFor), 8),
                LoadAndSaveEntry.Create(reader => _charsetColor = reader.ReadByte(), writer => writer.WriteByte(_charsetColor), 8),

                // _charsetBufPos was changed from byte to int
                LoadAndSaveEntry.Create(reader => _charsetBufPos = reader.ReadByte(), writer => writer.WriteByte(_charsetBufPos), 8, 9),
                LoadAndSaveEntry.Create(reader => _charsetBufPos = reader.ReadInt16(), writer => writer.WriteInt16(_charsetBufPos), 10),

                LoadAndSaveEntry.Create(reader => _haveMsg = reader.ReadByte(), writer => writer.WriteByte(_haveMsg), 8),
                LoadAndSaveEntry.Create(reader => _haveActorSpeechMsg = reader.ReadByte() != 0, writer => writer.WriteByte(_haveActorSpeechMsg), 61),
                LoadAndSaveEntry.Create(reader => _useTalkAnims = reader.ReadByte() != 0, writer => writer.WriteByte(_useTalkAnims), 8),

                LoadAndSaveEntry.Create(reader => _talkDelay = reader.ReadInt16(), writer => writer.WriteInt16(_talkDelay), 8),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 8),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 8, 27),
                LoadAndSaveEntry.Create(reader => SentenceNum = reader.ReadByte(), writer => writer.WriteByte(SentenceNum), 8),

                LoadAndSaveEntry.Create(reader => cutScene.SaveOrLoad(serializer), writer => cutScene.SaveOrLoad(serializer), 8),

                LoadAndSaveEntry.Create(reader => _numNestedScripts = reader.ReadByte(), writer => writer.WriteByte(_numNestedScripts), 8),
                LoadAndSaveEntry.Create(reader => _userPut = (sbyte)reader.ReadByte(), writer => writer.WriteByte(_userPut), 8),
                LoadAndSaveEntry.Create(reader => reader.ReadUInt16(), writer => writer.WriteUInt16(0), 17),
                LoadAndSaveEntry.Create(reader => _cursor.State = (sbyte)reader.ReadByte(), writer => writer.WriteByte(_cursor.State), 8),
                LoadAndSaveEntry.Create(reader => reader.ReadByte(), writer => writer.WriteByte(0), 8, 20),
                LoadAndSaveEntry.Create(reader => _currentCursor = reader.ReadByte(), writer => writer.WriteByte(_currentCursor), 8),
                LoadAndSaveEntry.Create(reader => _cursorData = reader.ReadBytes(8192), writer =>
                    {
                        var data = new byte[8192];
                        if (_cursorData != null)
                        {
                            Array.Copy(_cursorData, data, _cursorData.Length);
                        }
                        writer.Write(data);
                    }, 20),
                LoadAndSaveEntry.Create(reader => _cursor.Width = reader.ReadInt16(), writer => writer.WriteInt16(_cursor.Width), 20),
                LoadAndSaveEntry.Create(reader => _cursor.Height = reader.ReadInt16(), writer => writer.WriteInt16(_cursor.Height), 20),
                LoadAndSaveEntry.Create(reader => _cursor.Hotspot = new Point(reader.ReadInt16(), reader.ReadInt16()), writer =>
                    {
                        writer.WriteInt16(_cursor.Hotspot.X);
                        writer.WriteInt16(_cursor.Hotspot.Y);
                    }, 20),
                LoadAndSaveEntry.Create(reader => _cursor.Animate = reader.ReadByte() != 0, writer => writer.WriteByte(_cursor.Animate), 20),
                LoadAndSaveEntry.Create(reader => _cursor.AnimateIndex = reader.ReadByte(), writer => writer.WriteByte(_cursor.AnimateIndex), 20),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 20),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 20),

                LoadAndSaveEntry.Create(reader => reader.ReadBytes(256), writer => writer.Write(new byte[256]), 60),
                LoadAndSaveEntry.Create(reader => _doEffect = reader.ReadByte() != 0, writer => writer.WriteByte(_doEffect), 8),
                LoadAndSaveEntry.Create(reader => _switchRoomEffect = reader.ReadByte(), writer => writer.WriteByte(_switchRoomEffect), 8),
                LoadAndSaveEntry.Create(reader => _newEffect = reader.ReadByte(), writer => writer.WriteByte(_newEffect), 8),
                LoadAndSaveEntry.Create(reader => _switchRoomEffect2 = reader.ReadByte(), writer => writer.WriteByte(_switchRoomEffect2), 8),
                LoadAndSaveEntry.Create(reader => _bgNeedsRedraw = reader.ReadByte() != 0, writer => writer.WriteByte(_bgNeedsRedraw), 8),

                // The state of palManipulate is stored only since V10
                LoadAndSaveEntry.Create(reader => _palManipStart = reader.ReadByte(), writer => writer.WriteByte(_palManipStart), 10),
                LoadAndSaveEntry.Create(reader => _palManipEnd = reader.ReadByte(), writer => writer.WriteByte(_palManipEnd), 10),
                LoadAndSaveEntry.Create(reader => _palManipCounter = reader.ReadUInt16(), writer => writer.WriteUInt16(_palManipCounter), 10),

                // gfxUsageBits grew from 200 to 410 entries. Then 3 * 410 entries:
                LoadAndSaveEntry.Create(reader => Gdi.SaveOrLoad(serializer), writer => Gdi.SaveOrLoad(serializer), 0),

                LoadAndSaveEntry.Create(reader => Gdi.TransparentColor = reader.ReadByte(), writer => writer.WriteByte(Gdi.TransparentColor), 8, 50),
                LoadAndSaveEntry.Create(reader =>
                    {
                        for (int i = 0; i < 256; i++)
                        {
                            _currentPalette.Colors[i] = Color.FromRgb(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
                        }
                    }, writer =>
                    {
                        for (int i = 0; i < 256; i++)
                        {
                            var l_color = _currentPalette.Colors[i];
                            writer.WriteByte(l_color.R);
                            writer.WriteByte(l_color.G);
                            writer.WriteByte(l_color.B);
                        }
                    }, 8),
                LoadAndSaveEntry.Create(reader => reader.ReadBytes(768), writer => writer.Write(new byte[768]), 53),

                // Sam & Max specific palette replaced by _shadowPalette now.
                LoadAndSaveEntry.Create(reader => reader.ReadBytes(256), writer => writer.Write(new byte[256]), 8, 33),

                LoadAndSaveEntry.Create(reader => _charsetBuffer = reader.ReadBytes(256), writer => writer.WriteBytes(_charsetBuffer, 256), 8),

                LoadAndSaveEntry.Create(reader => EgoPositioned = reader.ReadByte() != 0, writer => writer.WriteByte(EgoPositioned), 8),

                // _gdi->_imgBufOffs grew from 4 to 5 entries. Then one day we realized
                // that we don't have to store it since initBGBuffers() recomputes it.
                LoadAndSaveEntry.Create(reader => reader.ReadUInt16s(4), writer => writer.WriteUInt16s(new ushort[4], 4), 8, 9),
                LoadAndSaveEntry.Create(reader => reader.ReadUInt16s(5), writer => writer.WriteUInt16s(new ushort[5], 5), 10, 26),

                // See _imgBufOffs: _numZBuffer is recomputed by initBGBuffers().
                LoadAndSaveEntry.Create(reader => Gdi.NumZBuffer = reader.ReadByte(), writer => writer.WriteByte(Gdi.NumZBuffer), 8, 26),

                LoadAndSaveEntry.Create(reader => _screenEffectFlag = reader.ReadByte() != 0, writer => writer.WriteByte(_screenEffectFlag), 8),

                LoadAndSaveEntry.Create(reader => reader.ReadByte(), writer => writer.WriteByte(0), 8, 9),
                LoadAndSaveEntry.Create(reader => reader.ReadByte(), writer => writer.WriteByte(0), 8, 9),

                // Converted _shakeEnabled to boolean and added a _shakeFrame field.
                LoadAndSaveEntry.Create(reader => _shakeEnabled = reader.ReadInt16() == 1, writer => writer.WriteInt16(_shakeEnabled ? 1 : 0), 8, 9),
                LoadAndSaveEntry.Create(reader => _shakeEnabled = reader.ReadBoolean(), writer => writer.WriteByte(_shakeEnabled), 10),
                LoadAndSaveEntry.Create(reader => _shakeFrame = (int)reader.ReadUInt32(), writer => writer.WriteUInt32((uint)_shakeFrame), 10),

                LoadAndSaveEntry.Create(reader => _keepText = reader.ReadByte() != 0, writer => writer.WriteByte(_keepText), 8),

                LoadAndSaveEntry.Create(reader => _screenB = reader.ReadUInt16(), writer => writer.WriteUInt16(_screenB), 8),
                LoadAndSaveEntry.Create(reader => _screenH = reader.ReadUInt16(), writer => writer.WriteUInt16(_screenH), 8),

                LoadAndSaveEntry.Create(reader => reader.ReadUInt16(), writer => writer.WriteUInt16(0), 47),

                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 9, 9),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 9, 9),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 9, 9),
                LoadAndSaveEntry.Create(reader => reader.ReadInt16(), writer => writer.WriteInt16(0), 9, 9)
            };

            #endregion MainEntries

            var md5Backup = new byte[16];
            Array.Copy(_gameMD5, md5Backup, 16);

            for (int i = 0; i < mainEntries.Length; i++)
            {
                mainEntries[i].Execute(serializer);
            }

            if (serializer.IsLoading)
            {
                roomData = _resManager.GetRoom(_roomResource);
            }
            //if (!Array.Equals(md5Backup, _gameMD5))
            //{
            //    //warning("Game was saved with different gamedata - you may encounter problems");
            //    //debug(1, "You have %s and save is %s.", md5str2, md5str1);
            //    return false;
            //}

            // Starting V14, we extended the usage bits, to be able to cope with games
            // that have more than 30 actors (up to 94 are supported now, in theory).
            // Since the format of the usage bits was changed by this, we have to
            // convert them when loading an older savegame.
//            if (serializer.IsLoading && serializer.Version < 14)
//                Gdi.UpgradeGfxUsageBits();

            // When loading, move the mouse to the saved mouse position.
            //if (serializer.Version >= 20)
            //{
            //    UpdateCursor();
            //    _system->warpMouse(_mouse.x, _mouse.y);
            //}

            // Before V61, we re-used the _haveMsg flag to handle "alternative" speech
            // sound files (see charset code 10).
            if (serializer.IsLoading && serializer.Version < 61)
            {
                if (_haveMsg == 0xFE)
                {
                    _haveActorSpeechMsg = false;
                    _haveMsg = 0xFF;
                }
                else
                {
                    _haveActorSpeechMsg = true;
                }
            }

            //
            // Save/load actors
            //
            for (int i = 0; i < Actors.Length; i++)
            {
                Actors[i].SaveOrLoad(serializer);
            }

            //
            // Save/load sound data
            //
            Sound.SaveOrLoad(serializer);

            //
            // Save/load script data
            //
            if (serializer.Version < 9)
            {
                for (int i = 0; i < 25; i++)
                {
                    _slots[i].SaveOrLoad(serializer, roomData.LocalScripts, ResourceManager.NumGlobalScripts);
                }
            }
            else if (serializer.Version < 20)
            {
                for (int i = 0; i < 40; i++)
                {
                    _slots[i].SaveOrLoad(serializer, roomData.LocalScripts, ResourceManager.NumGlobalScripts);
                }
            }
            else
            {
                for (int i = 0; i < NumScriptSlot; i++)
                {
                    _slots[i].SaveOrLoad(serializer, roomData.LocalScripts, ResourceManager.NumGlobalScripts);
                }
            }
            if (serializer.IsLoading)
            {
                _slots.ForEach(slot =>
                    {
                        if (slot.Where == WhereIsObject.Global)
                        {
                            slot.Offset -= 6;
                        }
                        else if (slot.Where == WhereIsObject.Local && slot.Number >= ResourceManager.NumGlobalScripts && roomData.LocalScripts[slot.Number - ResourceManager.NumGlobalScripts] != null)
                        {
                            slot.Offset = (uint)(slot.Offset - roomData.LocalScripts[slot.Number - ResourceManager.NumGlobalScripts].Offset);
                        }
                    });

                ResetRoomObjects();
            }

            //
            // Save/load local objects
            //
            for (int i = 0; i < _objs.Length; i++)
            {
                _objs[i].SaveOrLoad(serializer);
            }

            //
            // Save/load misc stuff
            //
            for (int i = 0; i < Verbs.Length; i++)
            {
                Verbs[i].SaveOrLoad(serializer);
            }
            for (int i = 0; i < 16; i++)
            {
                _nest[i].SaveOrLoad(serializer);
            }
            for (int i = 0; i < 6; i++)
            {
                _sentence[i].SaveOrLoad(serializer);
            }
            for (int i = 0; i < 6; i++)
            {
                _string[i].SaveOrLoad(serializer);
            }
            for (int i = 0; i < 16; i++)
            {
                _colorCycle[i].SaveOrLoad(serializer);
            }
            if (serializer.Version >= 13)
            {
                for (int i = 0; i < 20; i++)
                {
                    if (serializer.IsLoading)
                    {
                        _scaleSlots[i] = new ScaleSlot();
                    }
                    if (_scaleSlots[i] != null)
                    {
                        _scaleSlots[i].SaveOrLoad(serializer);
                    }
                }
            }

            //
            // Save/load resources
            //
            SaveOrLoadResources(serializer);

            //
            // Save/load global object state
            //
            var objStatesEntries = new[]
            {
                LoadAndSaveEntry.Create(reader =>
                    {
                        var objectOwnerTable = reader.ReadBytes(_resManager.ObjectOwnerTable.Length);
                        Array.Copy(objectOwnerTable, _resManager.ObjectOwnerTable, _resManager.ObjectOwnerTable.Length);
                    },
                    writer => writer.WriteBytes(_resManager.ObjectOwnerTable, _resManager.ObjectOwnerTable.Length)),
                LoadAndSaveEntry.Create(reader =>
                    {
                        var objectStateTable = reader.ReadBytes(_resManager.ObjectStateTable.Length);
                        Array.Copy(objectStateTable, _resManager.ObjectStateTable, _resManager.ObjectStateTable.Length);
                    },
                    writer => writer.WriteBytes(_resManager.ObjectStateTable, _resManager.ObjectStateTable.Length))
            };
            objStatesEntries.ForEach(e => e.Execute(serializer));

            //if (_objectRoomTable)
            //    s->saveLoadArrayOf(_objectRoomTable, _numGlobalObjects, sizeof(_objectRoomTable[0]), sleByte);

            //
            // Save/load palette data
            // Don't save 16 bit palette in FM-Towns and PCE games, since it gets regenerated afterwards anyway.
            //if (_16BitPalette && !(_game.platform == Common::kPlatformFMTowns && s->getVersion() < VER(82)) && !((_game.platform == Common::kPlatformFMTowns || _game.platform == Common::kPlatformPCEngine) && s->getVersion() > VER(87))) {
            //    s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16);
            //}

            var paletteEntries = new[]
            {
                LoadAndSaveEntry.Create(
                    reader => _shadowPalette = reader.ReadBytes(_shadowPalette.Length),
                    writer => writer.WriteBytes(_shadowPalette, _shadowPalette.Length)),
                // _roomPalette didn't show up until V21 save games
                // Note that we also save the room palette for Indy4 Amiga, since it
                // is used as palette map there too, but we do so slightly a bit
                // further down to group it with the other special palettes needed.
                LoadAndSaveEntry.Create(
                    reader => Gdi.RoomPalette = reader.ReadBytes(256),
                    writer => writer.WriteBytes(Gdi.RoomPalette, 256)
                        , 21),

                // PalManip data was not saved before V10 save games
                LoadAndSaveEntry.Create(reader =>
                    {
                        if (_palManipCounter != 0)
                        {
                            var colors = reader.ReadBytes(0x300);
                            for (int i = 0; i < 0x100; i++)
                            {
                                _palManipPalette.Colors[i] = Color.FromRgb(colors[i * 3], colors[i * 3 + 1], colors[i * 3 + 2]);    
                            }
                            var colors2 = reader.ReadUInt16s(0x300);
                            for (int i = 0; i < 0x100; i++)
                            {
                                _palManipIntermediatePal.Colors[i] = Color.FromRgb(colors2[i * 3], colors2[i * 3 + 1], colors2[i * 3 + 2]);    
                            }
                        }
                    },
                    writer =>
                    {
                        if (_palManipCounter != 0)
                        {
                            for (int i = 0; i < 0x100; i++)
                            {
                                writer.WriteByte(_palManipPalette.Colors[i].R);
                                writer.WriteByte(_palManipPalette.Colors[i].G);
                                writer.WriteByte(_palManipPalette.Colors[i].B);
                            }
                            for (int i = 0; i < 0x100; i++)
                            {
                                writer.WriteUInt16(_palManipIntermediatePal.Colors[i].R);
                                writer.WriteUInt16(_palManipIntermediatePal.Colors[i].G);
                                writer.WriteUInt16(_palManipIntermediatePal.Colors[i].B);
                            }
                        }
                    }, 10),


                // darkenPalette was not saved before V53
                LoadAndSaveEntry.Create(reader =>
                    {
                        // TODO?
                        //Array.Copy(currentPalette, darkenPalette, 768);
                    }, 0, 53),
                
            };
            paletteEntries.ForEach(entry => entry.Execute(serializer));

            // _colorUsedByCycle was not saved before V60
            if (serializer.IsLoading)
            {
                if (serializer.Version < 60)
                {
                    //Array.Clear(_colorUsedByCycle, 0, _colorUsedByCycle.Length);
                }
            }

            // Indy4 Amiga specific palette tables were not saved before V85
            //if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
            //    if (s->getVersion() >= 85) {
            //        s->saveLoadArrayOf(_roomPalette, 256, 1, sleByte);
            //        s->saveLoadArrayOf(_verbPalette, 256, 1, sleByte);
            //        s->saveLoadArrayOf(_amigaPalette, 3 * 64, 1, sleByte);

            //        // Starting from version 86 we also save the first used color in
            //        // the palette beyond the verb palette. For old versions we just
            //        // look for it again, which hopefully won't cause any troubles.
            //        if (s->getVersion() >= 86) {
            //            s->saveLoadArrayOf(&_amigaFirstUsedColor, 1, 2, sleUint16);
            //        } else {
            //            amigaPaletteFindFirstUsedColor();
            //        }
            //    } else {
            //        warning("Save with old Indiana Jones 4 Amiga palette handling detected");
            //        // We need to restore the internal state of the Amiga palette for Indy4
            //        // Amiga. This might lead to graphics glitches!
            //        setAmigaPaletteFromPtr(_currentPalette);
            //    }
            //}

            //
            // Save/load more global object state
            //
            var globalObjStatesEntries = new[]
            {
                LoadAndSaveEntry.Create(
                    reader => Array.Copy(reader.ReadUInt32s(_resManager.ClassData.Length), _resManager.ClassData, _resManager.ClassData.Length),
                    writer => writer.WriteUInt32s(_resManager.ClassData, _resManager.ClassData.Length))
            };
            globalObjStatesEntries.ForEach(entry => entry.Execute(serializer));

            //
            // Save/load script variables
            //
            var var120Backup = _variables[120];
            var var98Backup = _variables[98];

            //if (serializer.Version > 37)
            //{
            //    s->saveLoadArrayOf(_roomVars, _numRoomVariables, sizeof(_roomVars[0]), sleInt32);
            //}

            // The variables grew from 16 to 32 bit.
            var variablesEntries = new[]
            {
                LoadAndSaveEntry.Create(
                    reader => _variables = reader.ReadInt16s(_variables.Length).ConvertAll(s => (int)s),
                    writer => writer.WriteInt16s(_variables, _variables.Length)
                        , 0, 15),
                LoadAndSaveEntry.Create(
                    reader => _variables = reader.ReadInt32s(_variables.Length),
                    writer => writer.WriteInt32s(_variables, _variables.Length), 15),
                LoadAndSaveEntry.Create(
                    reader => _bitVars = new BitArray(reader.ReadBytes(_bitVars.Length / 8)),
                    writer => writer.Write(_bitVars.ToByteArray())
                ),
            };
            variablesEntries.ForEach(entry => entry.Execute(serializer));

            if (_game.GameId == GameId.Tentacle) // Maybe misplaced, but that's the main idea
            {
                _variables[120] = var120Backup;
            }
            if (_game.GameId == GameId.Indy4)
            {
                _variables[98] = var98Backup;
            }

            //
            // Save/load a list of the locked objects
            //
            var lockedObjEntries = new[]
            {
                LoadAndSaveEntry.Create(reader =>
                    {
                        ResType tmp;
                        while ((tmp = (ResType)reader.ReadByte()) != (ResType)0xFF)
                        {
                            var index = reader.ReadUInt16();
                            if (tmp == ResType.FlObject)
                            {
                                _objs[index].IsLocked = true;
                            }
                        }
                    },
                    writer =>
                    {
                        for (int i = 0; i < _objs.Length; i++)
                        {
                            if (_objs[i].IsLocked)
                            {
                                writer.WriteByte((byte)ResType.FlObject);
                                writer.WriteUInt16(i);
                            }
                        }
                        writer.Write((byte)0xFF);
                    }
                )
            };
            lockedObjEntries.ForEach(entry => entry.Execute(serializer));

            //
            // Save/load the Audio CD status
            //
            //if (serializer.Version >= 24)
            //{
            //    AudioCDManager::Status info;
            //    if (s->isSaving())
            //        info = _system->getAudioCDManager()->getStatus();
            //    s->saveLoadArrayOf(&info, 1, sizeof(info), audioCDEntries);
            //     If we are loading, and the music being loaded was supposed to loop
            //     forever, then resume playing it. This helps a lot when the audio CD
            //     is used to provide ambient music (see bug #788195).
            //    if (s->isLoading() && info.playing && info.numLoops < 0)
            //      _system->getAudioCDManager()->play(info.track, info.numLoops, info.start, info.duration);
            //}

            //
            // Save/load the iMuse status
            //
            if (IMuse != null && (_saveSound || !_saveTemporaryState))
            {
                IMuse.SaveOrLoad(serializer);
            }

            //
            // Save/load music engine status
            //
            if (MusicEngine != null)
            {
                MusicEngine.SaveOrLoad(serializer);
            }

            //
            // Save/load the charset renderer state
            //
            //if (s->getVersion() >= VER(73))
            //{
            //    _charset->saveLoadWithSerializer(s);
            //}
            //else if (s->isLoading())
            //{
            //    if (s->getVersion() == VER(72))
            //    {
            //        _charset->setCurID(s->loadByte());
            //    }
            //    else
            //    {
            //        // Before V72, the charset id wasn't saved. This used to cause issues such
            //        // as the one described in the bug report #1722153. For these savegames,
            //        // we reinitialize the id using a, hopefully, sane value.
            //        _charset->setCurID(_string[0]._default.charset);
            //    }
            //}
        }
ScummEngine