Exemplo n.º 1
0
        public void Box(int X, int Y, int Width, int Height, uint Value)
        {
            if ((X + Width <= 0) ||
                (X >= m_Width) ||
                (Y + Height <= 0) ||
                (Y >= m_Height))
            {
                return;
            }
            if (X < 0)
            {
                Width += X;
                X      = 0;
            }
            if (X + Width >= m_Width)
            {
                Width = m_Width - X;
            }
            if (Y < 0)
            {
                Height += Y;
                Y       = 0;
            }
            if (Y + Height >= m_Height)
            {
                Height = m_Height - Y;
            }
            switch (BitsPerPixel)
            {
            case 8:
                for (int i = 0; i < Height; ++i)
                {
                    for (int j = 0; j < Width; ++j)
                    {
                        m_ImageData.SetU8At(j + X + BytesPerLine * (Y + i), (byte)Value);
                    }
                }
                break;

            case 24:
                for (int i = 0; i < Height; ++i)
                {
                    for (int j = 0; j < Width; ++j)
                    {
                        m_ImageData.SetU8At((j + X) * 3 + BytesPerLine * (Y + i), (byte)(Value & 0xff));
                        m_ImageData.SetU8At((j + X) * 3 + BytesPerLine * (Y + i) + 1, (byte)((Value & 0xff00) >> 8));
                        m_ImageData.SetU8At((j + X) * 3 + BytesPerLine * (Y + i) + 2, (byte)((Value & 0xff0000) >> 16));
                    }
                }
                break;

            case 32:
                for (int i = 0; i < Height; ++i)
                {
                    for (int j = 0; j < Width; ++j)
                    {
                        m_ImageData.SetU32At((j + X) * 4 + BytesPerLine * (Y + i), Value);
                    }
                }
                break;

            default:
                throw new NotSupportedException("Bitdepth " + BitsPerPixel + " not supported yet");
            }
        }
Exemplo n.º 2
0
        public int ImageToMCBitmapData(Dictionary <int, List <ColorMappingTarget> > ForceBitPattern, List <Formats.CharData> Chars, bool[,] ErrornousBlocks, int CharX, int CharY, int WidthChars, int HeightChars, out GR.Memory.ByteBuffer bitmapData, out GR.Memory.ByteBuffer screenChar, out GR.Memory.ByteBuffer screenColor)
        {
            int numErrors = 0;

            ColorMappingTarget[] bitPattern = new ColorMappingTarget[3] {
                ColorMappingTarget.BITS_01, ColorMappingTarget.BITS_10, ColorMappingTarget.BITS_11
            };
            var usedBitPattern = new GR.Collections.Set <ColorMappingTarget>();

            Dictionary <int, GR.Collections.Set <ColorMappingTarget> > usedPatterns = new Dictionary <int, GR.Collections.Set <ColorMappingTarget> >();

            screenChar  = new GR.Memory.ByteBuffer((uint)(WidthChars * HeightChars));
            screenColor = new GR.Memory.ByteBuffer((uint)(WidthChars * HeightChars));
            bitmapData  = new GR.Memory.ByteBuffer((uint)(8 * WidthChars * HeightChars));

            GR.Collections.Map <byte, ColorMappingTarget> usedColors = new GR.Collections.Map <byte, ColorMappingTarget>();

            /*
             * ForceBitPattern = new Dictionary<int, byte>();
             * ForceBitPattern.Add( 4, 1 );
             * ForceBitPattern.Add( 1, 2 );
             * ForceBitPattern.Add( 3, 3 );*/

            for (int y = 0; y < HeightChars; ++y)
            {
                for (int x = 0; x < WidthChars; ++x)
                {
                    // ein zeichen-block
                    usedColors.Clear();
                    usedBitPattern.Clear();
                    if (ErrornousBlocks != null)
                    {
                        ErrornousBlocks[x, y] = false;
                    }
                    for (int charY = 0; charY < 8; ++charY)
                    {
                        for (int charX = 0; charX < 4; ++charX)
                        {
                            byte colorIndex = (byte)Image.GetPixel(x * 8 + charX * 2, y * 8 + charY);
                            if (colorIndex >= 16)
                            {
                                if (Chars != null)
                                {
                                    Chars[x + y * BlockWidth].Error = "Color index >= 16 (" + colorIndex + ") at " + (x * 8 + charX * 2).ToString() + ", " + (y * 8 + charY).ToString() + " (" + charX + "," + charY + ")";
                                }
                                if (ErrornousBlocks != null)
                                {
                                    ErrornousBlocks[x, y] = true;
                                }
                                ++numErrors;
                            }
                            if (colorIndex != BackgroundColor)
                            {
                                // remember used color
                                usedColors.Add(colorIndex, 0);
                            }
                        }
                    }
                    // more than 3 colors?
                    if (usedColors.Count > 3)
                    {
                        if (Chars != null)
                        {
                            Chars[x + y * BlockWidth].Error = "Too many colors used";
                        }
                        if (ErrornousBlocks != null)
                        {
                            ErrornousBlocks[x, y] = true;
                        }
                        ++numErrors;
                    }
                    else
                    {
                        if (usedColors.Count > 0)
                        {
                            int         colorTarget = 0;
                            List <byte> keys        = new List <byte>(usedColors.Keys);

                            // check for overlaps - two colors are used that would map to the same target pattern?
                            Dictionary <int, ColorMappingTarget> recommendedPattern = new Dictionary <int, ColorMappingTarget>();

                            numErrors += DetermineBestMapping(keys, x, y, ForceBitPattern, recommendedPattern, ErrornousBlocks);

                            foreach (byte colorIndex in keys)
                            {
                                if (recommendedPattern.ContainsKey(colorIndex))
                                {
                                    usedColors[colorIndex] = recommendedPattern[colorIndex];

                                    if (!usedPatterns.ContainsKey(colorIndex))
                                    {
                                        usedPatterns.Add(colorIndex, new GR.Collections.Set <ColorMappingTarget>());
                                    }
                                    usedPatterns[colorIndex].Add(recommendedPattern[colorIndex]);
                                    usedBitPattern.Add(recommendedPattern[colorIndex]);

                                    switch (recommendedPattern[colorIndex])
                                    {
                                    case ColorMappingTarget.BITS_01:
                                    {
                                        // upper screen char nibble
                                        byte value = screenChar.ByteAt(x + y * WidthChars);
                                        value &= 0x0f;
                                        value |= (byte)(colorIndex << 4);

                                        screenChar.SetU8At(x + y * WidthChars, value);
                                    }
                                    break;

                                    case ColorMappingTarget.BITS_10:
                                    {
                                        // lower nibble in screen char
                                        byte value = screenChar.ByteAt(x + y * WidthChars);
                                        value &= 0xf0;
                                        value |= (byte)(colorIndex);

                                        screenChar.SetU8At(x + y * WidthChars, value);
                                    }
                                    break;

                                    case ColorMappingTarget.BITS_11:
                                        // color ram
                                        screenColor.SetU8At(x + y * WidthChars, colorIndex);
                                        break;
                                    }
                                    continue;
                                }

                                if (!usedPatterns.ContainsKey(colorIndex))
                                {
                                    usedPatterns.Add(colorIndex, new GR.Collections.Set <ColorMappingTarget>());
                                }
                                usedPatterns[colorIndex].Add(bitPattern[colorTarget]);

                                colorTarget = 0;
                                while ((colorTarget < 3) &&
                                       (usedBitPattern.ContainsValue(bitPattern[colorTarget])))
                                {
                                    ++colorTarget;
                                }
                                usedBitPattern.Add(bitPattern[colorTarget]);

                                if (colorTarget == 0)
                                {
                                    // upper screen char nibble
                                    byte value = screenChar.ByteAt(x + y * WidthChars);
                                    value &= 0x0f;
                                    value |= (byte)(colorIndex << 4);

                                    screenChar.SetU8At(x + y * WidthChars, value);
                                    usedColors[colorIndex] = ColorMappingTarget.BITS_01;
                                }
                                else if (colorTarget == 1)
                                {
                                    // lower nibble in screen char
                                    byte value = screenChar.ByteAt(x + y * WidthChars);
                                    value &= 0xf0;
                                    value |= (byte)(colorIndex);

                                    screenChar.SetU8At(x + y * WidthChars, value);
                                    usedColors[colorIndex] = ColorMappingTarget.BITS_10;
                                }
                                else if (colorTarget == 2)
                                {
                                    // color ram
                                    screenColor.SetU8At(x + y * WidthChars, colorIndex);
                                    usedColors[colorIndex] = ColorMappingTarget.BITS_11;
                                }
                                ++colorTarget;
                            }
                        }
                        // write out bits

                        /*
                         * Debug.Log( "For Char " + x + "," + y );
                         * foreach ( var usedColor in usedColors )
                         * {
                         * Debug.Log( " Color " + usedColor.Key + " = " + usedColor.Value );
                         * }*/
                        for (int charY = 0; charY < 8; ++charY)
                        {
                            for (int charX = 0; charX < 4; ++charX)
                            {
                                byte colorIndex = (byte)Image.GetPixel(x * 8 + charX * 2, y * 8 + charY);
                                if (colorIndex != BackgroundColor)
                                {
                                    // other color
                                    byte colorValue = 0;

                                    switch (usedColors[colorIndex])
                                    {
                                    case ColorMappingTarget.BITS_01:
                                        colorValue = 0x01;
                                        break;

                                    case ColorMappingTarget.BITS_10:
                                        colorValue = 0x02;
                                        break;

                                    case ColorMappingTarget.BITS_11:
                                        colorValue = 0x03;
                                        break;
                                    }
                                    int bitmapIndex = x * 8 + y * 8 * WidthChars + charY;

                                    byte value = bitmapData.ByteAt(bitmapIndex);
                                    if (charX == 0)
                                    {
                                        value &= 0x3f;
                                        value |= (byte)(colorValue << 6);
                                    }
                                    else if (charX == 1)
                                    {
                                        value &= 0xcf;
                                        value |= (byte)(colorValue << 4);
                                    }
                                    else if (charX == 2)
                                    {
                                        value &= 0xf3;
                                        value |= (byte)(colorValue << 2);
                                    }
                                    else
                                    {
                                        value &= 0xfc;
                                        value |= colorValue;
                                    }
                                    bitmapData.SetU8At(bitmapIndex, value);
                                }
                            }
                        }
                    }
                }
            }

            /*
             * Debug.Log( "Used patterns:" );
             * foreach ( var entry in usedPatterns )
             * {
             * Debug.Log( "Index " + entry.Key );
             * foreach ( var pattern in entry.Value )
             * {
             *  Debug.Log( " used " + pattern );
             * }
             * }*/
            return(numErrors);
        }
Exemplo n.º 3
0
        public bool ExportMapsAsAssembly(bool Vertical, out string MapData, string LabelPrefix, bool WrapData, int WrapByteCount, string DataByteDirective)
        {
            bool hasExtraData = false;

            foreach (var map in Maps)
            {
                if (map.ExtraDataText.Length > 0)
                {
                    hasExtraData = true;
                    break;
                }
            }

            StringBuilder sbMaps = new StringBuilder();

            sbMaps.Append(LabelPrefix);
            sbMaps.Append("NUM_MAPS = ");
            sbMaps.AppendLine(Maps.Count.ToString());

            sbMaps.Append(LabelPrefix);
            sbMaps.AppendLine("MAP_LIST_LO");
            for (int i = 0; i < Maps.Count; ++i)
            {
                sbMaps.Append(DataByteDirective);
                sbMaps.Append(' ');
                sbMaps.AppendLine("<" + LabelPrefix + "MAP_" + NormalizeAsLabel(Maps[i].Name.ToUpper()));
            }
            sbMaps.AppendLine();
            sbMaps.Append(LabelPrefix);
            sbMaps.AppendLine("MAP_LIST_HI");
            for (int i = 0; i < Maps.Count; ++i)
            {
                sbMaps.Append(DataByteDirective);
                sbMaps.Append(' ');
                sbMaps.AppendLine(">" + LabelPrefix + "MAP_" + NormalizeAsLabel(Maps[i].Name.ToUpper()));
            }
            sbMaps.AppendLine();

            if (hasExtraData)
            {
                sbMaps.Append(LabelPrefix);
                sbMaps.AppendLine("MAP_EXTRA_DATA_LIST_LO");
                for (int i = 0; i < Maps.Count; ++i)
                {
                    sbMaps.Append(DataByteDirective);
                    sbMaps.Append(' ');
                    sbMaps.AppendLine("<" + LabelPrefix + "MAP_EXTRA_DATA_" + NormalizeAsLabel(Maps[i].Name.ToUpper()));
                }
                sbMaps.Append(LabelPrefix);
                sbMaps.AppendLine("MAP_EXTRA_DATA_LIST_HI");
                for (int i = 0; i < Maps.Count; ++i)
                {
                    sbMaps.Append(DataByteDirective);
                    sbMaps.Append(' ');
                    sbMaps.AppendLine(">" + LabelPrefix + "MAP_EXTRA_DATA_" + NormalizeAsLabel(Maps[i].Name.ToUpper()));
                }
                sbMaps.AppendLine();
            }


            for (int i = 0; i < Maps.Count; ++i)
            {
                var map = Maps[i];

                sbMaps.AppendLine();
                sbMaps.Append(LabelPrefix);
                sbMaps.AppendLine("MAP_" + NormalizeAsLabel(map.Name.ToUpper()));

                GR.Memory.ByteBuffer mapDataBuffer = new GR.Memory.ByteBuffer((uint)(map.Tiles.Width * map.Tiles.Height));

                if (Vertical)
                {
                    for (int y = 0; y < map.Tiles.Height; ++y)
                    {
                        for (int x = 0; x < map.Tiles.Width; ++x)
                        {
                            mapDataBuffer.SetU8At(x * map.Tiles.Height + y, (byte)map.Tiles[x, y]);
                        }
                    }
                }
                else
                {
                    for (int y = 0; y < map.Tiles.Height; ++y)
                    {
                        for (int x = 0; x < map.Tiles.Width; ++x)
                        {
                            mapDataBuffer.SetU8At(x + y * map.Tiles.Width, (byte)map.Tiles[x, y]);
                        }
                    }
                }
                sbMaps.AppendLine();
                sbMaps.Append(Util.ToASMData(mapDataBuffer, WrapData, WrapByteCount, DataByteDirective));
                if ((hasExtraData) &&
                    (map.ExtraDataText.Length > 0))
                {
                    sbMaps.AppendLine(";extra data");
                    sbMaps.Append(LabelPrefix);
                    sbMaps.AppendLine("MAP_EXTRA_DATA_" + NormalizeAsLabel(map.Name.ToUpper()));

                    // clean extra data
                    GR.Memory.ByteBuffer extraData = new GR.Memory.ByteBuffer();
                    string[]             lines     = map.ExtraDataText.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string line in lines)
                    {
                        string tempLine = line.Trim().Replace(" ", "");
                        if ((!tempLine.StartsWith(";")) &&
                            (!tempLine.StartsWith("#")) &&
                            (!tempLine.StartsWith("//")))
                        {
                            extraData.AppendHex(tempLine);
                        }
                    }

                    sbMaps.Append(Util.ToASMData(extraData, WrapData, WrapByteCount, DataByteDirective));
                    sbMaps.AppendLine();
                }
            }

            MapData = sbMaps.ToString();
            return(true);
        }
Exemplo n.º 4
0
        public int ImageToHiresBitmapData(List <Formats.CharData> Chars, bool[,] ErrornousBlocks, int CharX, int CharY, int WidthChars, int HeightChars, out GR.Memory.ByteBuffer bitmapData, out GR.Memory.ByteBuffer screenChar, out GR.Memory.ByteBuffer screenColor)
        {
            screenChar  = null;
            screenColor = null;
            bitmapData  = null;

            if ((CharX < 0) ||
                (CharX >= BlockWidth) ||
                (CharY < 0) ||
                (CharY >= BlockHeight) ||
                (WidthChars < 0) ||
                (HeightChars < 0) ||
                (CharX + WidthChars > BlockWidth) ||
                (CharY + HeightChars > BlockHeight))
            {
                return(1);
            }

            int numErrors = 0;

            screenChar  = new GR.Memory.ByteBuffer((uint)(WidthChars * HeightChars));
            screenColor = new GR.Memory.ByteBuffer((uint)(WidthChars * HeightChars));
            bitmapData  = new GR.Memory.ByteBuffer((uint)(8 * WidthChars * HeightChars));

            GR.Collections.Map <byte, byte> usedColors = new GR.Collections.Map <byte, byte>();

            for (int y = CharY; y < HeightChars; ++y)
            {
                for (int x = CharX; x < WidthChars; ++x)
                {
                    // ein zeichen-block
                    usedColors.Clear();
                    if (ErrornousBlocks != null)
                    {
                        ErrornousBlocks[x, y] = false;
                    }
                    for (int charY = 0; charY < 8; ++charY)
                    {
                        for (int charX = 0; charX < 8; ++charX)
                        {
                            byte colorIndex = (byte)Image.GetPixel(x * 8 + charX, y * 8 + charY);
                            if (colorIndex >= 16)
                            {
                                if (Chars != null)
                                {
                                    Chars[x + y * BlockWidth].Error = "Color index >= 16 (" + colorIndex + ") at " + (x * 8 + charX).ToString() + ", " + (y * 8 + charY).ToString() + " (" + charX + "," + charY + ")";
                                }
                                if (ErrornousBlocks != null)
                                {
                                    ErrornousBlocks[x, y] = true;
                                }
                                ++numErrors;
                            }
                            if (colorIndex != BackgroundColor)
                            {
                                // remember used color
                                usedColors.Add(colorIndex, 0);
                            }
                        }
                    }
                    // more than 2 colors?
                    if (usedColors.Count > 2)
                    {
                        if (Chars != null)
                        {
                            Chars[x + y * BlockWidth].Error = "Too many colors used";
                        }
                        if (ErrornousBlocks != null)
                        {
                            ErrornousBlocks[x, y] = true;
                        }
                        ++numErrors;
                    }
                    else
                    {
                        int firstColorIndex = -1;
                        if (usedColors.Count > 0)
                        {
                            int         colorTarget = 0;
                            List <byte> keys        = new List <byte>(usedColors.Keys);
                            foreach (byte colorIndex in keys)
                            {
                                if (colorTarget == 0)
                                {
                                    // upper screen char nibble
                                    byte value = screenChar.ByteAt(x + y * WidthChars);
                                    value &= 0x0f;
                                    value |= (byte)(colorIndex << 4);

                                    screenChar.SetU8At(x + y * WidthChars, value);
                                    usedColors[colorIndex] = 1;
                                    firstColorIndex        = colorIndex;
                                }
                                else if (colorTarget == 1)
                                {
                                    // lower nibble in screen char
                                    byte value = screenChar.ByteAt(x + y * WidthChars);
                                    value &= 0xf0;
                                    value |= (byte)(colorIndex);

                                    screenChar.SetU8At(x + y * WidthChars, value);
                                    usedColors[colorIndex] = 2;
                                }
                                ++colorTarget;
                            }
                        }
                        // write out bits
                        for (int charY = 0; charY < 8; ++charY)
                        {
                            for (int charX = 0; charX < 8; ++charX)
                            {
                                byte colorIndex = (byte)Image.GetPixel(x * 8 + charX, y * 8 + charY);
                                if (colorIndex == firstColorIndex)
                                {
                                    // other color
                                    byte colorValue  = usedColors[colorIndex];
                                    int  bitmapIndex = x * 8 + y * WidthChars * 8 + charY;

                                    byte value = bitmapData.ByteAt(bitmapIndex);
                                    int  mask  = (1 << (7 - charX));

                                    value &= (byte)(0xff ^ mask);
                                    value |= (byte)mask;
                                    bitmapData.SetU8At(bitmapIndex, value);
                                }
                            }
                        }
                    }
                }
            }
            return(numErrors);
        }
        private bool LoadVersion8(GR.Memory.ByteBuffer Data)
        {
            DisplayModeFile = (DisplayMode)Data.ByteAt(4);
            TileColorMode   = (ColorMode)Data.ByteAt(5);

            byte flags = Data.ByteAt(6);

            BackgroundColor  = Data.ByteAt(7);
            MultiColor1      = Data.ByteAt(8);
            MultiColor2      = Data.ByteAt(9);
            BackgroundColor4 = Data.ByteAt(10);

            BaseCellColorColorMatrix = Data.ByteAt(11);
            BaseCellColorScreenLo    = Data.ByteAt(12);
            BaseCellColorScreenHi    = Data.ByteAt(13);

            bool tileSysEnabled = ((flags & 0x01) != 0);


            ushort charDataBlockID      = 0xdab0;
            ushort charAttributeBlockID = 0xdab1;
            ushort charSetColorBlockID  = 0;
            ushort tileSetDataBlockID   = 0;
            ushort tileSetColorBlockID  = 0;
            ushort tileSetTagBlockID    = 0;
            ushort tileSetNameBlockID   = 0;
            ushort mapDataBlockID       = 0;

            int curBlockID = 0xdab2;

            if (TileColorMode == ColorMode.PER_CHAR)
            {
                charSetColorBlockID = (ushort)curBlockID++;
            }
            if (tileSysEnabled)
            {
                tileSetDataBlockID = (ushort)curBlockID++;
                if (TileColorMode == ColorMode.PER_TILE)
                {
                    tileSetColorBlockID = (ushort)curBlockID++;
                }
                tileSetTagBlockID  = (ushort)curBlockID++;
                tileSetNameBlockID = (ushort)curBlockID++;
            }
            mapDataBlockID = (ushort)curBlockID++;

            if (!tileSysEnabled)
            {
                // fake tiles (one per char)
                TileWidth  = 1;
                TileHeight = 1;
                NumTiles   = 256;

                for (int i = 0; i < NumTiles; ++i)
                {
                    Tile tile = new Tile();

                    tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                    tile.CharData.SetU16At(0, (ushort)i);

                    tile.ColorData.Resize((uint)(TileWidth * TileHeight));
                    tile.ColorData.SetU8At(0, (byte)CustomColor);

                    Tiles.Add(tile);
                }
            }

            var reader = Data.MemoryReader();

            reader.Skip(14);

            while (reader.DataAvailable)
            {
                ushort blockID = reader.ReadUInt16NetworkOrder();

                if (blockID == charDataBlockID)
                {
                    // Character data block

                    // CHARCNT: Character image count minus one( 16 - bit, LSBF ).
                    NumChars = reader.ReadUInt16() + 1;

                    // CHARDAT : Character image data( eight bytes / rows per image for CHARCNT images, rows are in TB order ).
                    for (int charIndex = 0; charIndex < NumChars; ++charIndex)
                    {
                        SingleChar newChar = new SingleChar();
                        newChar.Data  = new GR.Memory.ByteBuffer();
                        newChar.Color = CustomColor;
                        reader.ReadBlock(newChar.Data, 8);
                        Characters.Add(newChar);
                    }

                    if (!tileSysEnabled)
                    {
                        if (NumChars < 256)
                        {
                            // add all chars for safety reasons
                            for (int i = NumChars; i < 256; ++i)
                            {
                                SingleChar newChar = new SingleChar();
                                newChar.Data  = new GR.Memory.ByteBuffer(8);
                                newChar.Color = CustomColor;

                                Characters.Add(newChar);
                            }
                        }
                    }
                }
                else if (blockID == charAttributeBlockID)
                {
                    // char attributes
                    // BLKMARK: Block marker(0xDA, 0xB1).
                    // CHARATTS: Char attribute data, one byte per char image for CHARCNT images, low nybble = colour, high nybble = material.
                    //           nb.colours are only stored when the colouring mode is "per character".
                    for (int charIndex = 0; charIndex < NumChars; ++charIndex)
                    {
                        if (TileColorMode == ColorMode.PER_CHAR)
                        {
                            Characters[charIndex].Color = reader.ReadUInt8() & 0x0f;
                            if (!tileSysEnabled)
                            {
                                Tiles[charIndex].ColorData.SetU8At(0, (byte)Characters[charIndex].Color);
                            }
                        }
                    }
                }
                else if (blockID == charSetColorBlockID)
                {
                    // Character set colours block (only present if the project uses per-char colouring)...
                    //
                    // BLKMARK         : Block marker (0xDA, 0xBn).
                    // MTRXCOLRS_CHARS : Char colour data, 1-3 bytes per char image for CHARCNT images...
                    //
                    //    Colour_CmLo : Colour Matrix Low nybble (0-15) (not present if DISP_MODE is Bitmap_HR).
                    //    Colour_SmLo : Screen Matrix Low nybble (0-15) (only present if DISP_MODE is Bitmap_HR or Bitmap_MC).
                    //    Colour_SmHi : Screen Matrix High nybble (0-15) (only present if DISP_MODE is Bitmap_HR or Bitmap_MC).
                    //
                    //    Notes:-
                    //    - The colours in this block are intended for transfer to the C64 colour RAM cells and/or screen RAM cells.
                    //    - The usage / usefulness of a colour will depend on the display mode.
                    //    - Only the low nybbles of each byte are currently used, each provides a colour 0-15.

                    for (int i = 0; i < NumChars; ++i)
                    {
                        if (DisplayModeFile != DisplayMode.BITMAP_HIRES)
                        {
                            Characters[i].Color = reader.ReadUInt8();
                        }
                        if ((DisplayModeFile == DisplayMode.BITMAP_MC) ||
                            (DisplayModeFile == DisplayMode.BITMAP_HIRES))
                        {
                            // screen color lo
                            reader.ReadUInt8();
                            // screen color hi
                            reader.ReadUInt8();
                        }
                        if (!tileSysEnabled)
                        {
                            // use the charset color for our faked tiles
                            Tiles[i].ColorData.SetU8At(0, (byte)Characters[i].Color);
                        }
                    }
                }
                else if (blockID == tileSetDataBlockID)
                {
                    // Tile data block

                    // TILECNT: Tile count minus one(16 - bit, LSBF).
                    NumTiles = reader.ReadUInt16() + 1;

                    // TILEWID: Tile width( byte).
                    TileWidth = reader.ReadUInt8();
                    // TILEHEI: Tile height( byte).
                    TileHeight = reader.ReadUInt8();

                    // TILEDAT: Tile data, 16 bits per tile cell( LSBF) for TILEWID* TILEHEI cells * TILECNT items, cells are in LRTB order.
                    for (int i = 0; i < NumTiles; ++i)
                    {
                        Tile tile = new Tile();

                        tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                        tile.ColorData.Resize((uint)(TileWidth * TileHeight));

                        Tiles.Add(tile);
                    }
                    if (NumChars < 256)
                    {
                        // add all chars for safety reasons
                        for (int i = NumChars; i < 256; ++i)
                        {
                            SingleChar newChar = new SingleChar();
                            newChar.Data = new GR.Memory.ByteBuffer(8);
                            Characters.Add(newChar);
                        }
                    }
                    for (int i = 0; i < NumTiles; ++i)
                    {
                        for (int j = 0; j < TileWidth * TileHeight; ++j)
                        {
                            Tiles[i].CharData.SetU16At(j * 2, reader.ReadUInt16());
                        }
                    }
                }
                else if (blockID == tileSetColorBlockID)
                {
                    // Tile color block

                    // BLKMARK         : Block marker (0xDA, 0xBn).
                    // MTRXCOLRS_TILES : Tile colour data, 1-3 bytes per tile for TILECNT tiles...
                    //
                    //    Colour_CmLo : Colour Matrix Low nybble (0-15) (not present if DISP_MODE is Bitmap_HR).
                    //    Colour_SmLo : Screen Matrix Low nybble (0-15) (only present if DISP_MODE is Bitmap_HR or Bitmap_MC).
                    //    Colour_SmHi : Screen Matrix High nybble (0-15) (only present if DISP_MODE is Bitmap_HR or Bitmap_MC).
                    //
                    //    Notes:-
                    //    - The colours in this block are intended for transfer to the C64 colour RAM cells and/or screen RAM cells.
                    //    - The usage / usefulness of a colour will depend on the display mode.
                    //    - Only the low nybbles of each byte are currently used, each provides a colour 0-15.

                    for (int i = 0; i < NumTiles; ++i)
                    {
                        if (DisplayModeFile != DisplayMode.BITMAP_HIRES)
                        {
                            // tile generic color
                            reader.ReadUInt8();
                        }
                        if ((DisplayModeFile == DisplayMode.BITMAP_MC) ||
                            (DisplayModeFile == DisplayMode.BITMAP_HIRES))
                        {
                            // screen color lo
                            reader.ReadUInt8();
                            // screen color hi
                            reader.ReadUInt8();
                        }
                    }
                }
                else if (blockID == tileSetTagBlockID)
                {
                    // BLKMARK  : Block marker (0xDA, 0xBn).
                    // TILETAGS : Tile tag values, one byte per tile for TILECNT items.
                    for (int i = 0; i < NumTiles; ++i)
                    {
                        byte tileTag = reader.ReadUInt8();
                    }
                }
                else if (blockID == tileSetNameBlockID)
                {
                    // BLKMARK  : Block marker (0xDA, 0xBn).
                    // TILETAGS : Tile tag values, one byte per tile for TILECNT items.
                    for (int i = 0; i < NumTiles; ++i)
                    {
                        // zero terminated strings (urgh)
                        string name = "";
                        char   c    = (char)0;
                        do
                        {
                            c = (char)reader.ReadUInt8();
                            if (c != 0)
                            {
                                name += c;
                            }
                        }while (c != 0);
                        Tiles[i].Name = name;
                    }
                }
                else if (blockID == mapDataBlockID)
                {
                    // BLKMARK : Block marker (0xDA, 0xBn).
                    // MAPWID: Map Width(16 - bit, LSBF).
                    // MAPHEI: Map height(16 - bit, LSBF).
                    // MAPDAT: Map data, 16 bits per cell( LSBF ) for MAPWID* MAPHEI cells, cells are in LRTB order.
                    MapWidth  = reader.ReadUInt16();
                    MapHeight = reader.ReadUInt16();

                    MapData = new GR.Memory.ByteBuffer((uint)(MapWidth * MapHeight));
                    if (!tileSysEnabled)
                    {
                        // map has color data
                        MapColorData = new GR.Memory.ByteBuffer((uint)(MapWidth * MapHeight));
                    }

                    for (int i = 0; i < MapHeight; ++i)
                    {
                        for (int j = 0; j < MapWidth; ++j)
                        {
                            ushort mapData = reader.ReadUInt16();

                            // we only support 8 bytes per char
                            MapData.SetU8At(i * MapWidth + j, (byte)mapData); // do we?? Mega65!

                            if (TileColorMode == ColorMode.PER_CHAR)
                            {
                                MapColorData.SetU8At(i * MapWidth + j, (byte)Characters[(byte)mapData].Color);
                            }
                        }
                    }
                }
                else
                {
                    Debug.Log("Unexpected block ID: " + blockID.ToString("X"));
                    return(false);
                }
            }
            return(true);
        }
        private bool LoadVersion7(GR.Memory.ByteBuffer Data)
        {
            BackgroundColor  = Data.ByteAt(4);
            MultiColor1      = Data.ByteAt(5);
            MultiColor2      = Data.ByteAt(6);
            BackgroundColor4 = Data.ByteAt(7);
            CustomColor      = Data.ByteAt(8);
            TileColorMode    = (ColorMode)Data.ByteAt(9);

            // only uses values 0 to 2, which maps fine
            DisplayModeFile = (DisplayMode)Data.ByteAt(10);

            byte flags          = Data.ByteAt(11);
            bool tileSysEnabled = ((flags & 0x01) != 0);


            ushort charDataBlockID      = 0xdab0;
            ushort charAttributeBlockID = 0xdab1;
            ushort mapDataBlockID       = 0xdab2;


            if (!tileSysEnabled)
            {
                // fake tiles (one per char)
                TileWidth  = 1;
                TileHeight = 1;
                NumTiles   = 256;

                for (int i = 0; i < NumTiles; ++i)
                {
                    Tile tile = new Tile();

                    tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                    tile.CharData.SetU16At(0, (ushort)i);

                    tile.ColorData.Resize((uint)(TileWidth * TileHeight));
                    tile.ColorData.SetU8At(0, (byte)CustomColor);

                    Tiles.Add(tile);
                }
            }

            var reader = Data.MemoryReader();

            reader.Skip(12);

            while (reader.DataAvailable)
            {
                ushort blockID = reader.ReadUInt16NetworkOrder();

                if (blockID == mapDataBlockID)
                {
                    if (tileSysEnabled)
                    {
                        // Tile data block

                        // TILECNT: Tile count minus one(16 - bit, LSBF).
                        NumTiles = reader.ReadUInt16() + 1;

                        // TILEWID: Tile width( byte).
                        TileWidth = reader.ReadUInt8();
                        // TILEHEI: Tile height( byte).
                        TileHeight = reader.ReadUInt8();

                        // TILEDAT: Tile data, 16 bits per tile cell( LSBF) for TILEWID* TILEHEI cells * TILECNT items, cells are in LRTB order.
                        for (int i = 0; i < NumTiles; ++i)
                        {
                            Tile tile = new Tile();

                            tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                            tile.ColorData.Resize((uint)(TileWidth * TileHeight));

                            Tiles.Add(tile);
                        }
                        if (NumChars < 256)
                        {
                            // add all chars for safety reasons
                            for (int i = NumChars; i < 256; ++i)
                            {
                                SingleChar newChar = new SingleChar();
                                newChar.Data = new GR.Memory.ByteBuffer(8);
                                Characters.Add(newChar);
                            }
                        }
                        for (int i = 0; i < NumTiles; ++i)
                        {
                            for (int j = 0; j < TileWidth * TileHeight; ++j)
                            {
                                Tiles[i].CharData.SetU16At(j * 2, reader.ReadUInt16());
                            }
                        }
                    }
                    else
                    {
                        // BLKMARK : Block marker (0xDA, 0xBn).
                        // MAPWID: Map Width(16 - bit, LSBF).
                        // MAPHEI: Map height(16 - bit, LSBF).
                        // MAPDAT: Map data, 16 bits per cell( LSBF ) for MAPWID* MAPHEI cells, cells are in LRTB order.
                        MapWidth  = reader.ReadUInt16();
                        MapHeight = reader.ReadUInt16();

                        MapData = new GR.Memory.ByteBuffer((uint)(MapWidth * MapHeight));

                        for (int i = 0; i < MapHeight; ++i)
                        {
                            for (int j = 0; j < MapWidth; ++j)
                            {
                                MapData.SetU8At(i * MapWidth + j, (byte)reader.ReadUInt16());
                            }
                        }
                    }
                }

                if (blockID == charDataBlockID)
                {
                    // Character data block

                    // CHARCNT: Character image count minus one( 16 - bit, LSBF ).
                    NumChars = reader.ReadUInt16() + 1;

                    // CHARDAT : Character image data( eight bytes / rows per image for CHARCNT images, rows are in TB order ).
                    for (int charIndex = 0; charIndex < NumChars; ++charIndex)
                    {
                        SingleChar newChar = new SingleChar();
                        newChar.Data  = new GR.Memory.ByteBuffer();
                        newChar.Color = CustomColor;
                        reader.ReadBlock(newChar.Data, 8);
                        Characters.Add(newChar);
                    }

                    if (!tileSysEnabled)
                    {
                        if (NumChars < 256)
                        {
                            // add all chars for safety reasons
                            for (int i = NumChars; i < 256; ++i)
                            {
                                SingleChar newChar = new SingleChar();
                                newChar.Data  = new GR.Memory.ByteBuffer(8);
                                newChar.Color = CustomColor;

                                Characters.Add(newChar);
                            }
                        }
                    }
                }
                else if (blockID == charAttributeBlockID)
                {
                    // char attributes
                    // BLKMARK: Block marker(0xDA, 0xB1).
                    // CHARATTS: Char attribute data, one byte per char image for CHARCNT images, low nybble = colour, high nybble = material.
                    //           nb.colours are only stored when the colouring mode is "per character".
                    for (int charIndex = 0; charIndex < NumChars; ++charIndex)
                    {
                        if (TileColorMode == ColorMode.PER_CHAR)
                        {
                            Characters[charIndex].Color = reader.ReadUInt8() & 0x0f;
                            if (!tileSysEnabled)
                            {
                                Tiles[charIndex].ColorData.SetU8At(0, (byte)Characters[charIndex].Color);
                            }
                        }
                    }
                }
            }
            return(true);
        }
        private bool LoadVersion5(GR.Memory.ByteBuffer Data)
        {
            BackgroundColor = Data.ByteAt(4);
            MultiColor1     = Data.ByteAt(5);
            MultiColor2     = Data.ByteAt(6);
            CustomColor     = Data.ByteAt(7);
            TileColorMode   = (ColorMode)Data.ByteAt(8);

            byte flags   = Data.ByteAt(9);
            bool noTiles = ((flags & 0x01) == 0);

            DisplayModeFile = DisplayMode.HIRES;
            if ((flags & 0x04) != 0)
            {
                DisplayModeFile = DisplayMode.MULTICOLOR;
            }
            bool isExpanded = ((flags & 0x02) != 0);

            NumChars   = Data.UInt16At(10) + 1;
            NumTiles   = Data.UInt16At(12) + 1;
            TileWidth  = Data.ByteAt(14);
            TileHeight = Data.ByteAt(15);

            MapWidth  = Data.UInt16At(16);
            MapHeight = Data.UInt16At(18);

            if (noTiles)
            {
                // fake tiles
                TileWidth  = 1;
                TileHeight = 1;
                NumTiles   = 256;
            }


            int headerSize          = 20;
            int offsetToCharData    = headerSize;
            int offsetToCharAttribs = offsetToCharData + NumChars * 8;
            int offsetToTileData    = offsetToCharAttribs + NumChars;
            int offsetToTileColors  = offsetToTileData + NumTiles * TileWidth * TileHeight * 2;

            if ((isExpanded) ||
                (noTiles))
            {
                offsetToTileColors = offsetToTileData;
            }
            int offsetToMapData = offsetToTileColors + NumTiles;

            if ((TileColorMode != ColorMode.PER_TILE) ||
                (noTiles))
            {
                offsetToMapData = offsetToTileColors;
            }

            // char_data/char_attribs
            for (int charIndex = 0; charIndex < NumChars; ++charIndex)
            {
                SingleChar newChar = new SingleChar();
                newChar.Data = Data.SubBuffer(offsetToCharData + charIndex * 8, 8);
                if (TileColorMode == ColorMode.PER_CHAR)
                {
                    newChar.Color = Data.ByteAt(offsetToCharAttribs + charIndex) & 0x0f;
                }
                else
                {
                    newChar.Color = CustomColor;
                }

                Characters.Add(newChar);
            }

            // tile_data
            if (noTiles)
            {
                for (int i = 0; i < NumTiles; ++i)
                {
                    Tile tile = new Tile();

                    tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                    tile.CharData.SetU16At(0, (ushort)i);

                    tile.ColorData.Resize((uint)(TileWidth * TileHeight));
                    tile.ColorData.SetU8At(0, (byte)CustomColor);

                    Tiles.Add(tile);
                }
                if (NumChars < 256)
                {
                    // add all chars for safety reasons
                    for (int i = NumChars; i < 256; ++i)
                    {
                        SingleChar newChar = new SingleChar();
                        newChar.Data = new GR.Memory.ByteBuffer(8);
                        if (TileColorMode == ColorMode.PER_CHAR)
                        {
                            newChar.Color = Data.ByteAt(offsetToCharAttribs + i) & 0x0f;
                        }
                        else
                        {
                            newChar.Color = CustomColor;
                        }

                        Characters.Add(newChar);
                    }
                }
            }
            else
            {
                for (int i = 0; i < NumTiles; ++i)
                {
                    Tile tile = new Tile();

                    tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                    tile.ColorData.Resize((uint)(TileWidth * TileHeight));

                    Tiles.Add(tile);
                }

                if (isExpanded)
                {
                    byte curCharIndex = 0;
                    for (int i = 0; i < NumTiles; ++i)
                    {
                        for (int j = 0; j < TileWidth * TileHeight; ++j)
                        {
                            Tiles[i].CharData.SetU16At(j * 2, curCharIndex);
                            ++curCharIndex;
                        }
                    }
                }
                else
                {
                    // tile_data.      Size = NUM_TILES * TILE_WIDTH * TILE_HEIGHT bytes * 2 bytes. (only exists if CHAR_DATA is not "Expanded")
                    for (int i = 0; i < NumTiles; ++i)
                    {
                        for (int j = 0; j < TileWidth * TileHeight; ++j)
                        {
                            Tiles[i].CharData.SetU16At(j * 2, Data.UInt16At(offsetToTileData + i * TileWidth * TileHeight * 2 + j * 2));
                        }
                    }
                }
            }

            // TILE_COLOURS.   Size = NUM_TILES bytes (1 byte per tile = "RAM colour". only exists if COLOR_MODE = 1 (Per Tile)
            if (TileColorMode == ColorMode.PER_TILE)
            {
                for (int i = 0; i < NumTiles; ++i)
                {
                    for (int y = 0; y < TileHeight; ++y)
                    {
                        for (int x = 0; x < TileWidth; ++x)
                        {
                            Tiles[i].ColorData.SetU8At(x + y * TileWidth, (byte)(Data.ByteAt(offsetToTileColors + i) & 0x0f));
                        }
                    }
                }
            }
            else if (TileColorMode == ColorMode.PER_CHAR)
            {
                // with V5 this actually means per character
                for (int i = 0; i < NumTiles; ++i)
                {
                    for (int y = 0; y < TileHeight; ++y)
                    {
                        for (int x = 0; x < TileWidth; ++x)
                        {
                            byte charColor = (byte)Characters[Tiles[i].CharData.ByteAt(2 * (x + y * TileWidth))].Color;
                            Tiles[i].ColorData.SetU8At(x + y * TileWidth, charColor);
                        }
                    }
                }
            }
            else if (TileColorMode == ColorMode.GLOBAL)
            {
                for (int i = 0; i < NumTiles; ++i)
                {
                    for (int y = 0; y < TileHeight; ++y)
                    {
                        for (int x = 0; x < TileWidth; ++x)
                        {
                            Tiles[i].ColorData.SetU8At(x + y * TileWidth, (byte)CustomColor);
                        }
                    }
                }
            }

            /*
             * else if ( TileColorMode == ColorMode.PER_TILE_CELL )
             * {
             * for ( int i = 0; i < NumTiles; ++i )
             * {
             *  for ( int y = 0; y < TileHeight; ++y )
             *  {
             *    for ( int x = 0; x < TileWidth; ++x )
             *    {
             *      Tiles[i].ColorData.SetU8At( x + y * TileWidth, (byte)( Data.ByteAt( offsetCellAttribs + i * TileWidth * TileHeight + x + y * TileHeight ) & 0x0f ) );
             *    }
             *  }
             * }
             * }*/

            // MAP_DATA.        Size =  MAP_WID x MAP_HEI bytes.
            // tile indices are now 16 bit! for now force 8bit

            //MapData = Data.SubBuffer( offsetMapData, MapWidth * MapHeight * 2 );

            MapData = new GR.Memory.ByteBuffer((uint)(MapWidth * MapHeight));

            for (int i = 0; i < MapHeight; ++i)
            {
                for (int j = 0; j < MapWidth; ++j)
                {
                    MapData.SetU8At(i * MapWidth + j, Data.ByteAt(offsetToMapData + 2 * (i * MapWidth + j)));
                }
            }
            return(true);
        }
        private bool LoadVersion7(GR.Memory.ByteBuffer Data)
        {
            BackgroundColor  = Data.ByteAt(4);
            MultiColor1      = Data.ByteAt(5);
            MultiColor2      = Data.ByteAt(6);
            BackgroundColor4 = Data.ByteAt(7);
            CustomColor      = Data.ByteAt(8);
            TileColorMode    = (ColorMode)Data.ByteAt(9);

            CharsetMode = (TextMode)Data.ByteAt(10);

            byte flags          = Data.ByteAt(11);
            bool tileSysEnabled = ((flags & 0x01) != 0);


            ushort charDataBlockID      = 0xdab0;
            ushort charAttributeBlockID = 0xdab1;
            ushort mapDataBlockID       = 0xdab2;


            if (!tileSysEnabled)
            {
                // fake tiles (one per char)
                TileWidth  = 1;
                TileHeight = 1;
                NumTiles   = 256;

                for (int i = 0; i < NumTiles; ++i)
                {
                    Tile tile = new Tile();

                    tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                    tile.CharData.SetU16At(0, (ushort)i);

                    tile.ColorData.Resize((uint)(TileWidth * TileHeight));
                    tile.ColorData.SetU8At(0, (byte)CustomColor);

                    Tiles.Add(tile);
                }
            }

            var reader = Data.MemoryReader();

            reader.Skip(12);

            while (reader.DataAvailable)
            {
                ushort blockID = reader.ReadUInt16NetworkOrder();

                if (blockID == mapDataBlockID)
                {
                    if (tileSysEnabled)
                    {
                        // Tile data block

                        // TILECNT: Tile count minus one(16 - bit, LSBF).
                        NumTiles = reader.ReadUInt16() + 1;

                        // TILEWID: Tile width( byte).
                        TileWidth = reader.ReadUInt8();
                        // TILEHEI: Tile height( byte).
                        TileHeight = reader.ReadUInt8();

                        // TILEDAT: Tile data, 16 bits per tile cell( LSBF) for TILEWID* TILEHEI cells * TILECNT items, cells are in LRTB order.
                        for (int i = 0; i < NumTiles; ++i)
                        {
                            Tile tile = new Tile();

                            tile.CharData.Resize((uint)(TileWidth * TileHeight * 2));
                            tile.ColorData.Resize((uint)(TileWidth * TileHeight));

                            Tiles.Add(tile);
                        }
                        if (NumChars < 256)
                        {
                            // add all chars for safety reasons
                            for (int i = NumChars; i < 256; ++i)
                            {
                                SingleChar newChar = new SingleChar();
                                newChar.Data = new GR.Memory.ByteBuffer(8);
                                Characters.Add(newChar);
                            }
                        }
                        for (int i = 0; i < NumTiles; ++i)
                        {
                            for (int j = 0; j < TileWidth * TileHeight; ++j)
                            {
                                Tiles[i].CharData.SetU16At(j * 2, reader.ReadUInt16());
                            }
                        }
                    }
                    else
                    {
                        // BLKMARK : Block marker (0xDA, 0xBn).
                        // MAPWID: Map Width(16 - bit, LSBF).
                        // MAPHEI: Map height(16 - bit, LSBF).
                        // MAPDAT: Map data, 16 bits per cell( LSBF ) for MAPWID* MAPHEI cells, cells are in LRTB order.
                        MapWidth  = reader.ReadUInt16();
                        MapHeight = reader.ReadUInt16();

                        MapData = new GR.Memory.ByteBuffer((uint)(MapWidth * MapHeight));

                        for (int i = 0; i < MapHeight; ++i)
                        {
                            for (int j = 0; j < MapWidth; ++j)
                            {
                                MapData.SetU8At(i * MapWidth + j, (byte)reader.ReadUInt16());
                            }
                        }
                    }
                }

                if (blockID == charDataBlockID)
                {
                    // Character data block

                    // CHARCNT: Character image count minus one( 16 - bit, LSBF ).
                    NumChars = reader.ReadUInt16() + 1;

                    // CHARDAT : Character image data( eight bytes / rows per image for CHARCNT images, rows are in TB order ).
                    for (int charIndex = 0; charIndex < NumChars; ++charIndex)
                    {
                        SingleChar newChar = new SingleChar();
                        newChar.Data  = new GR.Memory.ByteBuffer();
                        newChar.Color = CustomColor;
                        reader.ReadBlock(newChar.Data, 8);
                        Characters.Add(newChar);
                    }

                    if (!tileSysEnabled)
                    {
                        if (NumChars < 256)
                        {
                            // add all chars for safety reasons
                            for (int i = NumChars; i < 256; ++i)
                            {
                                SingleChar newChar = new SingleChar();
                                newChar.Data  = new GR.Memory.ByteBuffer(8);
                                newChar.Color = CustomColor;

                                Characters.Add(newChar);
                            }
                        }
                    }
                }
                else if (blockID == charAttributeBlockID)
                {
                    // char attributes
                    // BLKMARK: Block marker(0xDA, 0xB1).
                    // CHARATTS: Char attribute data, one byte per char image for CHARCNT images, low nybble = colour, high nybble = material.
                    //           nb.colours are only stored when the colouring mode is "per character".
                    for (int charIndex = 0; charIndex < NumChars; ++charIndex)
                    {
                        if (TileColorMode == ColorMode.PER_TILE_CELL)
                        {
                            Characters[charIndex].Color = reader.ReadUInt8() & 0x0f;
                            if (!tileSysEnabled)
                            {
                                Tiles[charIndex].ColorData.SetU8At(0, (byte)Characters[charIndex].Color);
                            }
                        }
                    }
                }
            }


            /*
             *
             *
             * int headerSize = 20;
             * int offsetToCharData = headerSize;
             * int offsetToCharAttribs = offsetToCharData + NumChars * 8;
             * int offsetToTileData = offsetToCharAttribs + NumChars;
             * int offsetToTileColors = offsetToTileData + NumTiles * TileWidth * TileHeight * 2;
             * if ( ( tileSysEnabled )
             || ( noTiles ) )
             ||{
             ||offsetToTileColors = offsetToTileData;
             ||}
             ||int offsetToMapData = offsetToTileColors + NumTiles;
             ||if ( ( TileColorMode != ColorMode.PER_TILE )
             || ( noTiles ) )
             ||{
             ||offsetToMapData = offsetToTileColors;
             ||}
             ||
             ||// tile_data
             ||if ( noTiles )
             ||{
             ||for ( int i = 0; i < NumTiles; ++i )
             ||{
             || Tile tile = new Tile();
             ||
             || tile.CharData.Resize( (uint)( TileWidth * TileHeight * 2 ) );
             || tile.CharData.SetU16At( 0, (ushort)i );
             ||
             || tile.ColorData.Resize( (uint)( TileWidth * TileHeight ) );
             || tile.ColorData.SetU8At( 0, (byte)CustomColor );
             ||
             || Tiles.Add( tile );
             ||}
             ||if ( NumChars < 256 )
             ||{
             || // add all chars for safety reasons
             || for ( int i = NumChars; i < 256; ++i )
             || {
             ||   SingleChar    newChar = new SingleChar();
             ||   newChar.Data = new GR.Memory.ByteBuffer( 8 );
             ||   if ( TileColorMode == ColorMode.PER_TILE_CELL )
             ||   {
             ||     newChar.Color = Data.ByteAt( offsetToCharAttribs + i ) & 0x0f;
             ||   }
             ||   else
             ||   {
             ||     newChar.Color = CustomColor;
             ||   }
             ||
             ||   Characters.Add( newChar );
             || }
             ||}
             ||}
             ||else
             ||{
             ||for ( int i = 0; i < NumTiles; ++i )
             ||{
             || Tile tile = new Tile();
             ||
             || tile.CharData.Resize( (uint)( TileWidth * TileHeight * 2 ) );
             || tile.ColorData.Resize( (uint)( TileWidth * TileHeight ) );
             ||
             || Tiles.Add( tile );
             ||}
             ||
             ||if ( tileSysEnabled )
             ||{
             || byte curCharIndex = 0;
             || for ( int i = 0; i < NumTiles; ++i )
             || {
             ||   for ( int j = 0; j < TileWidth * TileHeight; ++j )
             ||   {
             ||     Tiles[i].CharData.SetU16At( j * 2, curCharIndex );
             ++curCharIndex;
             ||   }
             || }
             ||}
             ||else
             ||{
             || // tile_data.      Size = NUM_TILES * TILE_WIDTH * TILE_HEIGHT bytes * 2 bytes. (only exists if CHAR_DATA is not "Expanded")
             || for ( int i = 0; i < NumTiles; ++i )
             || {
             ||   for ( int j = 0; j < TileWidth * TileHeight; ++j )
             ||   {
             ||     Tiles[i].CharData.SetU16At( j * 2, Data.UInt16At( offsetToTileData + i * TileWidth * TileHeight * 2 + j * 2 ) );
             ||   }
             || }
             ||}
             ||}
             ||
             ||// TILE_COLOURS.   Size = NUM_TILES bytes (1 byte per tile = "RAM colour". only exists if COLOR_MODE = 1 (Per Tile)
             ||if ( TileColorMode == ColorMode.PER_TILE )
             ||{
             ||for ( int i = 0; i < NumTiles; ++i )
             ||{
             || for ( int y = 0; y < TileHeight; ++y )
             || {
             ||   for ( int x = 0; x < TileWidth; ++x )
             ||   {
             ||     Tiles[i].ColorData.SetU8At( x + y * TileWidth, (byte)( Data.ByteAt( offsetToTileColors + i ) & 0x0f ) );
             ||   }
             || }
             ||}
             ||}
             ||else if ( TileColorMode == ColorMode.PER_TILE_CELL )
             ||{
             ||// with V5 this actually means per character
             ||for ( int i = 0; i < NumTiles; ++i )
             ||{
             || for ( int y = 0; y < TileHeight; ++y )
             || {
             ||   for ( int x = 0; x < TileWidth; ++x )
             ||   {
             ||     byte    charColor = (byte)Characters[Tiles[i].CharData.ByteAt( 2 * ( x + y * TileWidth ) )].Color;
             ||     Tiles[i].ColorData.SetU8At( x + y * TileWidth, charColor );
             ||   }
             || }
             ||}
             ||}
             ||else if ( TileColorMode == ColorMode.GLOBAL )
             ||{
             ||for ( int i = 0; i < NumTiles; ++i )
             ||{
             || for ( int y = 0; y < TileHeight; ++y )
             || {
             ||   for ( int x = 0; x < TileWidth; ++x )
             ||   {
             ||     Tiles[i].ColorData.SetU8At( x + y * TileWidth, (byte)CustomColor );
             ||   }
             || }
             ||}
             ||}
             ||else if ( TileColorMode == ColorMode.PER_TILE_CELL )
             ||{
             ||for ( int i = 0; i < NumTiles; ++i )
             ||{
             || for ( int y = 0; y < TileHeight; ++y )
             || {
             ||   for ( int x = 0; x < TileWidth; ++x )
             ||   {
             ||     Tiles[i].ColorData.SetU8At( x + y * TileWidth, (byte)( Data.ByteAt( offsetCellAttribs + i * TileWidth * TileHeight + x + y * TileHeight ) & 0x0f ) );
             ||   }
             || }
             ||}
             ||}
             ||
             */
            return(true);
        }