Exemple #1
0
        static Graphic ReadGraphic(GraphicReader graphicReader, IDataReader file, int width, int height,
                                   bool alpha, bool texture, bool reset)
        {
            var graphic = new Graphic
            {
                Width          = width,
                Height         = height,
                IndexedGraphic = true
            };

            if (reset)
            {
                file.Position = 0;
            }

            graphicReader.ReadGraphic(graphic, file, new GraphicInfo
            {
                Width         = width,
                Height        = height,
                GraphicFormat = texture ? GraphicFormat.Texture4Bit : GraphicFormat.Palette4Bit,
                PaletteOffset = 0,
                Alpha         = alpha
            });

            return(graphic);
        }
Exemple #2
0
        public OutroData(IGameData gameData)
        {
            var outroHunks = AmigaExecutable.Read(gameData.Files["Ambermoon_extro"].Files[1]);
            var codeHunks  = outroHunks.Where(h => h.Type == AmigaExecutable.HunkType.Code)
                             .Select(h => new DataReader(((AmigaExecutable.Hunk)h).Data))
                             .ToList();
            var dataHunks = outroHunks
                            .Where(h => h.Type == AmigaExecutable.HunkType.Data)
                            .Select(h => new DataReader(((AmigaExecutable.Hunk)h).Data))
                            .ToList();
            var graphicReader = new GraphicReader();
            var graphicInfo   = new GraphicInfo
            {
                GraphicFormat = GraphicFormat.Palette5Bit,
                Alpha         = false,
                PaletteOffset = 0
            };
            var dataHunk         = dataHunks[0];
            var imageHunk        = dataHunks[1];
            var actionCache      = new Dictionary <uint, List <OutroAction> >();
            var imageDataOffsets = new List <uint>();

            Graphic LoadPalette(DataReader hunk)
            {
                var paletteGraphic = new Graphic();

                graphicReader.ReadGraphic(paletteGraphic, hunk, paletteGraphicInfo);
                return(paletteGraphic);
            }

            LoadFonts(codeHunks[0]);

            #region Hunk 0 - Actions and texts

            // Initial palette (all zeros)
            outroPalettes.Add(LoadPalette(dataHunk));

            // There are actually 3 outro sequence lists dependent on if Valdyn
            // is in the party and if you found the yellow teleporter sphere.
            for (int i = 0; i < 3; ++i)
            {
                var sequence = new List <OutroAction>();

                while (true)
                {
                    uint actionListOffset = dataHunk.ReadBEUInt32();

                    if (actionListOffset == 0)
                    {
                        break;
                    }

                    uint imageDataOffset = dataHunk.ReadBEUInt32();

                    if (!imageDataOffsets.Contains(imageDataOffset))
                    {
                        imageDataOffsets.Add(imageDataOffset);
                    }

                    sequence.Add(new OutroAction
                    {
                        Command     = OutroCommand.ChangePicture,
                        ImageOffset = imageDataOffset
                    });

                    if (actionCache.TryGetValue(actionListOffset, out var cachedActions))
                    {
                        sequence.AddRange(cachedActions);
                    }
                    else
                    {
                        int readPosition = dataHunk.Position;
                        dataHunk.Position = (int)actionListOffset;
                        var actions = new List <OutroAction>();

                        while (true)
                        {
                            byte scrollAmount = dataHunk.ReadByte();

                            if (scrollAmount == 0xff)
                            {
                                actions.Add(new OutroAction
                                {
                                    Command = OutroCommand.WaitForClick
                                });
                                break;
                            }

                            int    textDisplayX = dataHunk.ReadByte();
                            bool   largeText    = dataHunk.ReadByte() != 0;
                            string text         = dataHunk.ReadNullTerminatedString();
                            int?   textIndex    = text.Length == 0 ? (int?)null : texts.Count;

                            if (text.Length != 0)
                            {
                                texts.Add(text);
                            }

                            actions.Add(new OutroAction
                            {
                                Command      = OutroCommand.PrintTextAndScroll,
                                LargeText    = largeText,
                                TextIndex    = textIndex,
                                ScrollAmount = scrollAmount + 1
                            });
                        }

                        sequence.AddRange(actions);
                        actionCache.Add(actionListOffset, actions);
                        dataHunk.Position = readPosition;
                    }
                }

                outroActions.Add((OutroOption)i, sequence.AsReadOnly());
            }

            #endregion

            #region Hunk 1 - Images

            Graphic LoadGraphic(int width, int height)
            {
                graphicInfo.Width  = width;
                graphicInfo.Height = height;
                var graphic = new Graphic();

                graphicReader.ReadGraphic(graphic, imageHunk, graphicInfo);
                return(graphic);
            }

            foreach (var imageDataOffset in imageDataOffsets)
            {
                imageHunk.Position = (int)imageDataOffset;
                int width  = imageHunk.ReadBEUInt16() * 16;
                int height = imageHunk.ReadBEUInt16();
                imageHunk.Position += 2; // unused word
                byte paletteIndex = (byte)outroPalettes.Count;
                outroPalettes.Add(LoadPalette(imageHunk));
                graphics.Add(imageDataOffset, KeyValuePair.Create(LoadGraphic(width, height), paletteIndex));
            }

            #endregion
        }
Exemple #3
0
        public IntroData(GameData gameData)
        {
            var introHunks = AmigaExecutable.Read(gameData.Files["Ambermoon_intro"].Files[1])
                             .Where(h => h.Type == AmigaExecutable.HunkType.Data).Select(h => new DataReader(((AmigaExecutable.Hunk)h).Data))
                             .ToList();
            var graphicReader = new GraphicReader();

            #region Hunk 0 - Palettes and texts

            var hunk0 = introHunks[0];

            Graphic LoadPalette()
            {
                var paletteGraphic = new Graphic();

                graphicReader.ReadGraphic(paletteGraphic, hunk0, paletteGraphicInfo);
                return(paletteGraphic);
            }

            for (int i = 0; i < 9; ++i)
            {
                introPalettes.Add(LoadPalette());
            }

            hunk0.Position += 8; // 8 unknown bytes

            for (int i = 0; i < 8; ++i)
            {
                byte startByte = hunk0.PeekByte();
                if (startByte != 0x20 && (startByte < 'A' || startByte > 'Z'))
                {
                    ++hunk0.Position; // Sometimes there is an unknown start byte
                }
                texts.Add((IntroText)i, hunk0.ReadNullTerminatedString());
            }

            if (hunk0.ReadByte() != 4) // Should contain the amount of main menu text (= 4)
            {
                throw new AmbermoonException(ExceptionScope.Data, "Wrong intro data.");
            }

            for (int i = 0; i < 4; ++i)
            {
                // The 4 main menu texts are prefixed by 2 bytes (x and y render offset).
                hunk0.Position += 2; // We skip those bytes here.
                texts.Add((IntroText)(8 + i), hunk0.ReadNullTerminatedString());
            }

            // TODO: the credits will follow

            #endregion

            #region Hunk 1 - Main menu background and town graphics

            Size[] hunk1ImageSizes = new Size[8]
            {
                new Size(96, 300), // not sure
                new Size(320, 256),
                new Size(160, 128),
                new Size(160, 128),
                new Size(160, 128),
                new Size(160, 128),
                new Size(160, 128),
                new Size(160, 128)
            };

            for (int i = 0; i < 8; ++i)
            {
                var reader = introHunks[1];

                if (reader.PeekDword() == 0x494d5021) // "IMP!", may be imploded
                {
                    reader = new DataReader(Deploder.DeplodeFimp(reader).Reverse().ToArray());
                }

                var graphicInfo = new GraphicInfo
                {
                    Width         = hunk1ImageSizes[i].Width,
                    Height        = hunk1ImageSizes[i].Height,
                    GraphicFormat = GraphicFormat.Palette4Bit,
                    PaletteOffset = 0,
                    Alpha         = false
                };
                var graphic = new Graphic();
                graphicReader.ReadGraphic(graphic, reader, graphicInfo);
                graphics.Add((IntroGraphic)i, graphic);
            }

            #endregion

            #region Hunk 2 - Unknown

            // TODO

            #endregion

            #region Hunk 3 - Intro graphics (planets, etc)

            Size[] hunk3ImageSizes = new Size[6]
            {
                new Size(128, 82),  // Thalion Logo
                new Size(64, 64),   // Sun
                new Size(128, 128), // Lyramion
                new Size(64, 64),   // Morag
                new Size(64, 64),   // Forest Moon
                new Size(96, 96),   // Meteor
                // TODO ...
            };
            int[] hunk3FrameCounts = new int[6]
            {
                1,
                12,
                1,
                1,
                1,
                1,
                // TODO ...
            };

            for (int i = 0; i < 6; ++i)
            {
                var graphicInfo = new GraphicInfo
                {
                    Width         = hunk3ImageSizes[i].Width,
                    Height        = hunk3ImageSizes[i].Height,
                    GraphicFormat = GraphicFormat.Palette4Bit,
                    PaletteOffset = 0,
                    Alpha         = false
                };
                Graphic graphic;
                int     frames = hunk3FrameCounts[i];
                if (frames == 1)
                {
                    graphic = new Graphic();
                    graphicReader.ReadGraphic(graphic, introHunks[3], graphicInfo);
                }
                else
                {
                    graphic = new Graphic(frames * graphicInfo.Width, graphicInfo.Height, 0);

                    for (int f = 0; f < frames; ++f)
                    {
                        var frameGraphic = new Graphic();
                        graphicReader.ReadGraphic(frameGraphic, introHunks[3], graphicInfo);
                        graphic.AddOverlay((uint)(f * frameGraphic.Width), 0, frameGraphic, false);
                    }
                }
                graphics.Add(IntroGraphic.ThalionLogo + i, graphic);
                if (i == 0) // The other graphics start at 0x42B8 (the data between is unknown yet)
                {
                    introHunks[3].Position = 0x42B8;
                }
            }

            // TODO ...

            #endregion
        }
Exemple #4
0
        public void ReadLabdata(Labdata labdata, IDataReader dataReader, IGameData gameData)
        {
            labdata.WallHeight        = dataReader.ReadWord();
            labdata.CombatBackground  = dataReader.ReadWord() & 0x0fu;
            labdata.CeilingColorIndex = dataReader.ReadByte();
            labdata.FloorColorIndex   = dataReader.ReadByte();
            // Note: The ceiling texture index can be 0 in which case a sky is used.
            //       The sky is composed of a color gradient and a lab background
            //       which is given inside the map data.
            // To be more precisely if the texture index (ceiling and also floor) is
            // 0, the color index is used to draw instead. For example the town of
            // S'Angrila doesn't use a floor texture but only a color.
            uint ceilingTextureIndex = dataReader.ReadByte();
            uint floorTextureIndex   = dataReader.ReadByte();

            labdata.Objects.Clear();
            int numObjects = dataReader.ReadWord();
            var objects    = new List <Tuple <ushort, List <Tuple <short, short, short, int> > > >(numObjects);

            for (int i = 0; i < numObjects; ++i)
            {
                var obj = Tuple.Create(dataReader.ReadWord(), new List <Tuple <short, short, short, int> >(8));

                for (int n = 0; n < 8; ++n) // 8 sub entries (a map object can consist of up to 8 sub objects)
                {
                    obj.Item2.Add(Tuple.Create(
                                      (short)dataReader.ReadWord(),
                                      (short)dataReader.ReadWord(),
                                      (short)dataReader.ReadWord(),
                                      (int)dataReader.ReadWord()));
                }

                objects.Add(obj);
            }

            labdata.ObjectInfos.Clear();
            int numObjectInfos = dataReader.ReadWord();

            for (int i = 0; i < numObjectInfos; ++i)
            {
                var objectInfo = new Labdata.ObjectInfo
                {
                    Flags               = (Tileset.TileFlags)dataReader.ReadDword(),
                    TextureIndex        = dataReader.ReadWord(),
                    NumAnimationFrames  = dataReader.ReadByte(),
                    ColorIndex          = dataReader.ReadByte(),
                    TextureWidth        = dataReader.ReadByte(),
                    TextureHeight       = dataReader.ReadByte(),
                    MappedTextureWidth  = dataReader.ReadWord(),
                    MappedTextureHeight = dataReader.ReadWord()
                };

                labdata.ObjectInfos.Add(objectInfo);
            }

            foreach (var obj in objects)
            {
                var subObjects = new List <Labdata.ObjectPosition>(8);

                foreach (var pos in obj.Item2)
                {
                    if (pos.Item4 != 0)
                    {
                        subObjects.Add(new Labdata.ObjectPosition
                        {
                            X      = pos.Item1,
                            Y      = pos.Item2,
                            Z      = pos.Item3,
                            Object = labdata.ObjectInfos[pos.Item4 - 1]
                        });
                    }
                }

                labdata.Objects.Add(new Labdata.Object
                {
                    AutomapType = (AutomapType)obj.Item1,
                    SubObjects  = subObjects
                });
            }

            labdata.Walls.Clear();
            int numWalls = dataReader.ReadWord();

            for (int i = 0; i < numWalls; ++i)
            {
                var wallData = new Labdata.WallData
                {
                    Flags        = (Tileset.TileFlags)dataReader.ReadDword(),
                    TextureIndex = dataReader.ReadByte(),
                    AutomapType  = (AutomapType)dataReader.ReadByte(),
                    ColorIndex   = dataReader.ReadByte()
                };
                int numOverlays = dataReader.ReadByte();
                if (numOverlays != 0)
                {
                    wallData.Overlays = new Labdata.OverlayData[numOverlays];

                    for (int o = 0; o < numOverlays; ++o)
                    {
                        wallData.Overlays[o] = new Labdata.OverlayData
                        {
                            Blend         = dataReader.ReadByte() != 0,
                            TextureIndex  = dataReader.ReadByte(),
                            PositionX     = dataReader.ReadByte(),
                            PositionY     = dataReader.ReadByte(),
                            TextureWidth  = dataReader.ReadByte(),
                            TextureHeight = dataReader.ReadByte()
                        };
                    }
                }
                labdata.Walls.Add(wallData);
            }

            // Load labyrinth graphics
            var graphicReader = new GraphicReader();

            if (floorTextureIndex != 0)
            {
                labdata.FloorGraphic = ReadGraphic(graphicReader, gameData.Files["Floors.amb"].Files[(int)floorTextureIndex], 64, 64, false, false, true);
            }
            if (ceilingTextureIndex != 0)
            {
                labdata.CeilingGraphic = ReadGraphic(graphicReader, gameData.Files["Floors.amb"].Files[(int)ceilingTextureIndex], 64, 64, false, false, true);
            }
            var objectTextureFiles = gameData.Files[$"2Object3D.amb"].Files;

            gameData.Files[$"3Object3D.amb"].Files.ToList().ForEach(f => objectTextureFiles[f.Key] = f.Value);
            labdata.ObjectGraphics.Clear();
            foreach (var objectInfo in labdata.ObjectInfos)
            {
                if (objectInfo.NumAnimationFrames == 1)
                {
                    labdata.ObjectGraphics.Add(ReadGraphic(graphicReader, objectTextureFiles[(int)objectInfo.TextureIndex],
                                                           (int)objectInfo.TextureWidth, (int)objectInfo.TextureHeight, true, true, true));
                }
                else
                {
                    var compoundGraphic = new Graphic((int)objectInfo.NumAnimationFrames * (int)objectInfo.TextureWidth,
                                                      (int)objectInfo.TextureHeight, 0);

                    for (uint i = 0; i < objectInfo.NumAnimationFrames; ++i)
                    {
                        var partialGraphic = ReadGraphic(graphicReader, objectTextureFiles[(int)objectInfo.TextureIndex],
                                                         (int)objectInfo.TextureWidth, (int)objectInfo.TextureHeight, true, true, i == 0);

                        compoundGraphic.AddOverlay(i * objectInfo.TextureWidth, 0u, partialGraphic, false);
                    }

                    labdata.ObjectGraphics.Add(compoundGraphic);
                }
            }
            var wallTextureFiles    = gameData.Files[$"2Wall3D.amb"].Files;
            var overlayTextureFiles = gameData.Files[$"2Overlay3D.amb"].Files;

            gameData.Files[$"3Wall3D.amb"].Files.ToList().ForEach(f => wallTextureFiles[f.Key]       = f.Value);
            gameData.Files[$"3Overlay3D.amb"].Files.ToList().ForEach(f => overlayTextureFiles[f.Key] = f.Value);
            labdata.WallGraphics.Clear();
            int wallIndex = 0;

            foreach (var wall in labdata.Walls)
            {
                var wallGraphic = ReadGraphic(graphicReader, wallTextureFiles[(int)wall.TextureIndex],
                                              128, 80, wall.Flags.HasFlag(Tileset.TileFlags.Transparency), true, true);

                labdata.WallGraphics.Add(wallGraphic);

                if (wall.Overlays != null && wall.Overlays.Length != 0)
                {
                    foreach (var overlay in wall.Overlays)
                    {
                        wallGraphic.AddOverlay(overlay.PositionX, overlay.PositionY, ReadGraphic(graphicReader,
                                                                                                 overlayTextureFiles[(int)overlay.TextureIndex], (int)overlay.TextureWidth, (int)overlay.TextureHeight, true, true, true),
                                               overlay.Blend);
                    }
                }

                ++wallIndex;
            }
        }