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); }
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 }
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 }
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; } }