// Second data hunk, right behind UITexts. internal Buttons(IDataReader dataReader) { var graphicInfo = new GraphicInfo { Width = 32, Height = 13, Alpha = true, GraphicFormat = GraphicFormat.Palette3Bit, PaletteOffset = 24 }; var graphicReader = new GraphicReader(); Graphic ReadGraphic(IDataReader dataReader) { var graphic = new Graphic(); graphicReader.ReadGraphic(graphic, dataReader, graphicInfo); return(graphic); } foreach (var buttonType in Enum.GetValues <ButtonType>()) { entries.Add(buttonType, ReadGraphic(dataReader)); } dataReader.AlignToWord(); }
internal Cursors(IDataReader dataReader) { var graphicReader = new GraphicReader(); var graphicInfo = new GraphicInfo { Width = 16, Height = 16, Alpha = true, GraphicFormat = GraphicFormat.Palette3Bit, PaletteOffset = 24 }; Graphic ReadGraphic() { var graphic = new Graphic(); graphicReader.ReadGraphic(graphic, dataReader, graphicInfo); return(graphic); } for (int i = 0; i < Count; ++i) { entries.Add(new Cursor { HotspotX = (short)dataReader.ReadWord(), HotspotY = (short)dataReader.ReadWord(), Graphic = ReadGraphic() }); } }
Graphic LoadGraphic(Monster monster) { var file = gameData.Files["Monster_gfx.amb"].Files[(int)monster.CombatGraphicIndex]; file.Position = 0; var graphic = new Graphic(); var graphicReader = new GraphicReader(); var graphicInfo = new GraphicInfo { Width = (int)monster.FrameWidth, Height = (int)monster.FrameHeight, GraphicFormat = GraphicFormat.Palette5Bit, Alpha = true, PaletteOffset = 0 }; int numFrames = file.Size / ((graphicInfo.Width * graphicInfo.Height * 5 + 7) / 8); // TODO: is this inside monster data? var compoundGraphic = new Graphic(numFrames * (int)monster.MappedFrameWidth, (int)monster.MappedFrameHeight, 0); for (int i = 0; i < numFrames; ++i) { graphicReader.ReadGraphic(graphic, file, graphicInfo); compoundGraphic.AddOverlay((uint)i * monster.MappedFrameWidth, 0, graphic.CreateScaled((int)monster.MappedFrameWidth, (int)monster.MappedFrameHeight), false); } for (int i = 0; i < compoundGraphic.Data.Length; ++i) { compoundGraphic.Data[i] = monster.MonsterPalette[compoundGraphic.Data[i] & 0x1f]; } return(compoundGraphic); }
/* Some interesting offsets: * * 2nd data hunk * ============= * * Offsets are for German 1.05. * * 0x7AA0: Palette indices for event pix (only 8 of 9) * 0x8085: Name of the dictionary file. From here on the relative * text offsets will differ between german and english version! */ public ExecutableData(List <AmigaExecutable.IHunk> hunks) { var firstCodeHunk = hunks.FirstOrDefault(h => h.Type == AmigaExecutable.HunkType.Code); if (firstCodeHunk == null) { DataInfoString = "Unknown data version"; } else { var infoReader = new DataReader((firstCodeHunk as AmigaExecutable.Hunk?)?.Data); infoReader.Position = 6; DataVersionString = infoReader.ReadNullTerminatedString(AmigaExecutable.Encoding); DataInfoString = infoReader.ReadNullTerminatedString(AmigaExecutable.Encoding); } var reloc32Hunk = (AmigaExecutable.Reloc32Hunk?)hunks.LastOrDefault(h => h.Type == AmigaExecutable.HunkType.RELOC32); var dataHunkReaders = hunks.Where(h => h.Type == AmigaExecutable.HunkType.Data) .Select(h => new DataReader(((AmigaExecutable.Hunk)h).Data)).ToArray(); int dataHunkIndex = 0; if (reloc32Hunk == null || !reloc32Hunk.Value.Entries.ContainsKey(5) || reloc32Hunk.Value.Entries[5].Count != 15) { throw new AmbermoonException(ExceptionScope.Data, "Unexpected executable format."); } var relocOffsets = reloc32Hunk.Value.Entries[5]; uint digitOffset = relocOffsets.Take(9).Aggregate((a, b) => a + b) + 0x76D; // TODO: does this work for all versions? uint codepageOffset = relocOffsets.Take(12).Aggregate((a, b) => a + b); uint textOffset = codepageOffset + relocOffsets.Skip(12).Take(2).Aggregate((a, b) => a + b) + 4; uint glyphOffset = codepageOffset + relocOffsets.Skip(12).Aggregate((a, b) => a + b) + 262; if (glyphOffset % 4 != 0) { glyphOffset += 4 - glyphOffset % 4; } dataHunkIndex = 0; UIGraphics = Read <UIGraphics>(dataHunkReaders, ref dataHunkIndex); dataHunkIndex = 1; dataHunkReaders[1].Position = (int)digitOffset; DigitGlyphs = Read <DigitGlyphs>(dataHunkReaders, ref dataHunkIndex); // TODO ... dataHunkReaders[1].Position = (int)glyphOffset; Glyphs = Read <Glyphs>(dataHunkReaders, ref dataHunkIndex); Cursors = Read <Cursors>(dataHunkReaders, ref dataHunkIndex); // Here are the 3 builtin palettes for primary UI, automap and secondary UI. for (int i = 0; i < 3; ++i) { BuiltinPalettes[i] = GraphicProvider.ReadPalette(dataHunkReaders[dataHunkIndex]); } // Then 9 vertical color gradients used for skies are stored. They are stored // as 16 bit XRGB colors and not color indices! // The first 3 skies are for Lyramion, the next 3 for the forest moon and the last // 3 for Morag. The first sky is night, the second twilight and the third day. // Transitions blend night with twilight or day with twilight. var skyGraphicInfo = new GraphicInfo { Alpha = false, GraphicFormat = GraphicFormat.XRGB16, Width = 1, Height = 72 }; var graphicReader = new GraphicReader(); List <uint> colors = new List <uint>(); for (int i = 0; i < 9; ++i) { var sky = SkyGradients[i] = new Graphic(); graphicReader.ReadGraphic(sky, dataHunkReaders[dataHunkIndex], skyGraphicInfo); } // After the 9 sky gradients there are 6 partial palettes (16 colors). // Two of them per world (first for night, second for twilight). // They are also blended together (the first 16 colors of the map's palette is // used for day) and then replaces the first 16 colors of the map's palette. var daytimePaletteReplacementInfo = new GraphicInfo { Alpha = false, GraphicFormat = GraphicFormat.XRGB16, Width = 1, Height = 16 }; for (int i = 0; i < 6; ++i) { var replacement = DaytimePaletteReplacements[i] = new Graphic(); graphicReader.ReadGraphic(replacement, dataHunkReaders[dataHunkIndex], daytimePaletteReplacementInfo); } // TODO ... const string search = "Amberfiles/"; dataHunkReaders[1].Position = (int)dataHunkReaders[1].FindString(search, dataHunkReaders[1].Position) + search.Length + 50; FileList = Read <FileList>(dataHunkReaders, ref dataHunkIndex); WorldNames = Read <WorldNames>(dataHunkReaders, ref dataHunkIndex); Messages = Read <Messages>(dataHunkReaders, ref dataHunkIndex); AutomapNames = Read <AutomapNames>(dataHunkReaders, ref dataHunkIndex); OptionNames = Read <OptionNames>(dataHunkReaders, ref dataHunkIndex); SongNames = Read <SongNames>(dataHunkReaders, ref dataHunkIndex); SpellTypeNames = Read <SpellTypeNames>(dataHunkReaders, ref dataHunkIndex); SpellNames = Read <SpellNames>(dataHunkReaders, ref dataHunkIndex); LanguageNames = Read <LanguageNames>(dataHunkReaders, ref dataHunkIndex); ClassNames = Read <ClassNames>(dataHunkReaders, ref dataHunkIndex); RaceNames = Read <RaceNames>(dataHunkReaders, ref dataHunkIndex); AbilityNames = Read <AbilityNames>(dataHunkReaders, ref dataHunkIndex); AttributeNames = Read <AttributeNames>(dataHunkReaders, ref dataHunkIndex); AbilityNames.AddShortNames(dataHunkReaders[dataHunkIndex]); AttributeNames.AddShortNames(dataHunkReaders[dataHunkIndex]); ItemTypeNames = Read <ItemTypeNames>(dataHunkReaders, ref dataHunkIndex); AilmentNames = Read <AilmentNames>(dataHunkReaders, ref dataHunkIndex); UITexts = Read <UITexts>(dataHunkReaders, ref dataHunkIndex); Buttons = Read <Buttons>(dataHunkReaders, ref dataHunkIndex); int itemCount = dataHunkReaders[dataHunkIndex].ReadWord(); if (dataHunkReaders[dataHunkIndex].ReadWord() != itemCount || itemCount != 402) { throw new AmbermoonException(ExceptionScope.Data, "Invalid item data."); } var itemReader = new ItemReader(); var items = new Dictionary <uint, Item>(); for (uint i = 1; i <= 402; ++i) // there are 402 items { items.Add(i, Item.Load(i, itemReader, dataHunkReaders[dataHunkIndex])); } ItemManager = new ItemManager(items); }
internal UIGraphics(IDataReader dataReader) { var graphicReader = new GraphicReader(); var graphicInfo = new GraphicInfo { Width = 16, Height = 6, Alpha = true, GraphicFormat = GraphicFormat.Palette3Bit, PaletteOffset = 24 }; Graphic ReadGraphic(IDataReader dataReader, byte maskColor = 0) { var graphic = new Graphic(); graphicReader.ReadGraphic(graphic, dataReader, graphicInfo, maskColor); return(graphic); } Graphic ReadOpaqueGraphic(IDataReader dataReader) { var graphic = new Graphic(); graphicReader.ReadGraphic(graphic, dataReader, graphicInfo); graphic.ReplaceColor(0, 32); return(graphic); } // Note: First 156 bytes seem to be some offsets etc. dataReader.Position = 156; entries.Add(UIGraphic.DisabledOverlay16x6, ReadGraphic(dataReader)); graphicInfo.Height = 16; // window frames entries.Add(UIGraphic.FrameUpperLeft, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameLeft, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameLowerLeft, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameTop, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameBottom, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameUpperRight, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameRight, ReadGraphic(dataReader)); entries.Add(UIGraphic.FrameLowerRight, ReadGraphic(dataReader)); graphicInfo.GraphicFormat = GraphicFormat.Palette5Bit; graphicInfo.PaletteOffset = 0; for (int i = (int)UIGraphic.StatusDead; i <= (int)UIGraphic.StatusRangeAttack; ++i) { entries.Add((UIGraphic)i, ReadGraphic(dataReader)); } graphicInfo.Width = 32; graphicInfo.Height = 29; graphicInfo.GraphicFormat = GraphicFormat.Palette5Bit; graphicInfo.PaletteOffset = 0; entries.Add(UIGraphic.Eagle, ReadGraphic(dataReader)); graphicInfo.Height = 26; entries.Add(UIGraphic.Explosion, ReadGraphic(dataReader)); graphicInfo.Height = 23; graphicInfo.GraphicFormat = GraphicFormat.Palette3Bit; graphicInfo.PaletteOffset = 24; entries.Add(UIGraphic.Ouch, ReadGraphic(dataReader)); graphicInfo.Width = 16; graphicInfo.Height = 60; entries.Add(UIGraphic.StarBlinkAnimation, ReadGraphic(dataReader)); graphicInfo.Height = 40; entries.Add(UIGraphic.PlusBlinkAnimation, ReadGraphic(dataReader)); graphicInfo.Height = 36; entries.Add(UIGraphic.LeftPortraitBorder, ReadGraphic(dataReader)); entries.Add(UIGraphic.CharacterValueBarFrames, ReadGraphic(dataReader)); entries.Add(UIGraphic.RightPortraitBorder, ReadGraphic(dataReader)); graphicInfo.Height = 1; entries.Add(UIGraphic.SmallBorder1, ReadGraphic(dataReader)); entries.Add(UIGraphic.SmallBorder2, ReadGraphic(dataReader)); graphicInfo.Height = 16; for (int i = (int)UIGraphic.Candle; i <= (int)UIGraphic.Map; ++i) { entries.Add((UIGraphic)i, ReadOpaqueGraphic(dataReader)); } graphicInfo.Width = 32; graphicInfo.Height = 15; entries.Add(UIGraphic.Windchain, ReadOpaqueGraphic(dataReader)); graphicInfo.Height = 32; entries.Add(UIGraphic.MonsterEyeInactive, ReadOpaqueGraphic(dataReader)); entries.Add(UIGraphic.MonsterEyeActive, ReadOpaqueGraphic(dataReader)); entries.Add(UIGraphic.Night, ReadOpaqueGraphic(dataReader)); entries.Add(UIGraphic.Dusk, ReadOpaqueGraphic(dataReader)); entries.Add(UIGraphic.Day, ReadOpaqueGraphic(dataReader)); entries.Add(UIGraphic.Dawn, ReadOpaqueGraphic(dataReader)); graphicInfo.Height = 17; graphicInfo.Alpha = true; entries.Add(UIGraphic.ButtonFrame, ReadGraphic(dataReader)); entries.Add(UIGraphic.ButtonFramePressed, ReadGraphic(dataReader)); // Note: There is a 1-bit mask here where a 0 bit means transparent (keep color) and 1 means overlay. // As we use this for buttons we will set the color as the button back color (28). // The disable overlay is 32x11 in size. var disableOverlay = new Graphic(32, 11, 0); for (int y = 0; y < 11; ++y) { var bits = dataReader.ReadDword(); for (int x = 0; x < 32; ++x) { if ((bits & 0x80000000) != 0) { disableOverlay.Data[y * 32 + x] = 28; } bits <<= 1; } } entries.Add(UIGraphic.ButtonDisabledOverlay, disableOverlay); graphicInfo.Width = 32; graphicInfo.Height = 32; entries.Add(UIGraphic.Compass, ReadOpaqueGraphic(dataReader)); graphicInfo.Width = 16; graphicInfo.Height = 9; entries.Add(UIGraphic.Attack, ReadGraphic(dataReader)); entries.Add(UIGraphic.Defense, ReadGraphic(dataReader)); graphicInfo.Width = 32; graphicInfo.Height = 34; entries.Add(UIGraphic.Skull, ReadGraphic(dataReader, 25)); entries.Add(UIGraphic.EmptyCharacterSlot, ReadOpaqueGraphic(dataReader)); graphicInfo.Width = 16; graphicInfo.Height = 16; graphicInfo.GraphicFormat = GraphicFormat.Palette3Bit; graphicInfo.PaletteOffset = 0; var compoundGraphic = new Graphic(176, 16, 0); for (uint i = 0; i < 11; ++i) { compoundGraphic.AddOverlay(i * 16u, 0u, ReadGraphic(dataReader), false); } entries.Add(UIGraphic.ItemConsume, compoundGraphic); graphicInfo.Width = 32; graphicInfo.Height = 29; graphicInfo.GraphicFormat = GraphicFormat.Palette5Bit; graphicInfo.PaletteOffset = 0; entries.Add(UIGraphic.Talisman, ReadGraphic(dataReader)); graphicInfo.Width = 16; graphicInfo.Height = 47; graphicInfo.GraphicFormat = GraphicFormat.Palette3Bit; graphicInfo.PaletteOffset = 24; entries.Add(UIGraphic.UnknownChain, ReadGraphic(dataReader)); graphicInfo.Width = 8; graphicInfo.Height = 84; entries.Add(UIGraphic.BorderWithTriangles, ReadGraphic(dataReader)); }