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 }