Пример #1
0
        // read a block of the given index from the current LBX file into to the given LBXBlock object
        public void read(LBXBlock block, int index)
        {
            if (_fileIn != null)
            {
                if (index < _blockCount)
                {
                    // some LBX files have 0-length blocks, so check for that
                    if (_blockSizes[index] > 0)
                    {
                        _fileIn.BaseStream.Position = _blockOffsets[index];
                        block.data = _fileIn.ReadBytes(_blockSizes[index]);
                    }
                    else
                    {
                        block.data = null;
                    }

                    block.filePath = _filePath;
                    block.index    = index;
                    block.offset   = _blockOffsets[index];
                    block.size     = _blockSizes[index];
                }
                else
                {
                    throw new IndexOutOfRangeException("Requested block index out of bounds.");
                }
            }
            else
            {
                throw new Exception("No LBX file opened.");
            }
        }
Пример #2
0
        public void load(string filePath, int index)
        {
            LBXReader lbx   = new LBXReader(filePath);
            LBXBlock  block = new LBXBlock();

            lbx.read(block, index);
            load(block);
            lbx.close();
        }
Пример #3
0
        private void loadPalettes()
        {
            if (gameFolderPathTextBox.Text.Length > 0 && Directory.Exists(gameFolderPathTextBox.Text))
            {
                // populate the list box with the font .LBX files and the first several blocks.
                // all of the game fonts are stored here
                palettesListBox.Items.Clear();
                //palettesListBox.Items.Add("FONTS.LBX:000");
                palettesListBox.Items.Add("FONTS.LBX:001");
                palettesListBox.Items.Add("FONTS.LBX:002");
                palettesListBox.Items.Add("FONTS.LBX:003");
                palettesListBox.Items.Add("FONTS.LBX:004");
                palettesListBox.Items.Add("FONTS.LBX:005");

                palettesListBox.Items.Add("IFONTS.LBX:000");
                palettesListBox.Items.Add("IFONTS.LBX:001");
                palettesListBox.Items.Add("IFONTS.LBX:002");
                palettesListBox.Items.Add("IFONTS.LBX:003");
                palettesListBox.Items.Add("IFONTS.LBX:004");

                // a Palette[] holds the actual Palette objects
                palettes = new Palette[palettesListBox.Items.Count];
                LBXBlock block = new LBXBlock();

                // load those
                for (int i = 0; i < palettes.Length; ++i)
                {
                    // create new palette, then parse out the filename from the block index
                    palettes[i] = new Palette();
                    string listItem     = (string)palettesListBox.Items[i];
                    string fontFileName = listItem.Substring(0, listItem.IndexOf(":"));
                    int    blockIndex   = Int32.Parse(listItem.Substring(listItem.IndexOf(":") + 1, listItem.Length - listItem.IndexOf(":") - 1));

                    lbxReader.close();
                    lbxReader.open(gameFolderPathTextBox.Text + "\\" + fontFileName);
                    lbxReader.read(block, blockIndex);

                    palettes[i].load(block);
                }
                lbxReader.close();

                // set the second font as the current, which should also trigger the event to
                // update currentPaletteIndex and redraw the palette view image
                palettesListBox.SetSelected(2, true);
            }
        }
Пример #4
0
        /// <summary>
        /// Whoa, does this work?
        /// <para>paratextwee?</para>
        /// </summary>
        private void updateLBX()
        {
            LBXBlock block = new LBXBlock();

            // load new LBX
            lbxReader.close();
            lbxReader.open(gameFolderPathTextBox.Text + "\\" + currentLBXFileName);
            currentLBXBlock = 0;

            // update LBX file size label
            long size = lbxReader.getFileSize();

            if (size < 1048576)
            {
                float kbCount = (float)size / 1024.0f;
                currentLBXFileSizeLabel.Text = String.Format("{0:G4} KB", kbCount);
            }
            else
            {
                float mbCount = (float)size / 1048576.0f;
                currentLBXFileSizeLabel.Text = String.Format("{0:G4} MB", mbCount);
            }

            // enable or disable arrows accordingly
            if (lbxReader.getBlockCount() <= 1)
            {
                prevLBXBlockButton.Enabled = false;
                nextLBXBlockButton.Enabled = false;
            }
            else
            {
                prevLBXBlockButton.Enabled = false;
                nextLBXBlockButton.Enabled = true;
            }

            updateBlock();
        }
Пример #5
0
        /// <summary>
        /// Load a full 256-entry palette from the data contained in the given LBXBlock.
        /// </summary>
        /// <param name="block"></param>
        public void load(LBXBlock block)
        {
            if (block != null)
            {
                for (int i = 0; i < 256; ++i)
                {
                    /*
                     * // convert ARGB [0,63] to RGBA [0,255]
                     * uint color = ((uint)block.data[i * 4 + 1] << 2) +
                     *           ((uint)block.data[i * 4 + 2] << 10) +
                     *           ((uint)block.data[i * 4 + 3] << 18) +
                     *           ((uint)block.data[i * 4 + 0] << 24);
                     * */
                    // convert ARGB [0,63] to ARGB [0,255]
                    uint a = (uint)block.data[i * 4 + 0];
                    uint r = (uint)block.data[i * 4 + 1] << 2;
                    uint g = (uint)block.data[i * 4 + 2] << 2;
                    uint b = (uint)block.data[i * 4 + 3] << 2;

                    //if (a == 1)
                    a = 255;

                    //_palette[i] = r + (g << 8) + (b << 16) + (a << 24);
                    // BGRA ????????

                    this._palette[i] = b + (g << 8) + (r << 16) + (a << 24);
                }

                this.size   = 256;
                this.offset = 0;
            }
            else
            {
                throw new Exception("Null block given to load");
            }
        }
Пример #6
0
        public void load(LBXBlock block)
        {
            if (block.size > 0 && block.data != null)
            {
                free();
                BinaryReader data = new BinaryReader(new MemoryStream(block.data));

                // read header information
                _header.width      = data.ReadUInt16();
                _header.height     = data.ReadUInt16();
                _header.zero       = data.ReadUInt16();
                _header.frameCount = data.ReadUInt16();
                _header.frameDelay = data.ReadUInt16();
                _header.flags      = data.ReadUInt16();

                // only support 640x480 images for now
                if (_header.width <= 640 && _header.height <= 480)
                {
                    _frames = new ImageFrame[_header.frameCount];

                    // read frame offsets, plus the extra at the end
                    uint start, end;
                    start = data.ReadUInt32();

                    for (int i = 0; i < _header.frameCount; ++i)
                    {
                        end               = data.ReadUInt32();
                        _frames[i].size   = end - start;
                        _frames[i].offset = start;
                        start             = end;
                    }

                    // if there's an internal palette, it follows the offsets and precedes the frame data
                    if ((_header.flags & (ushort)ImageFlags.INTERNAL_PALETTE) != 0)
                    {
                        // color shift specifies where to start overwriting the palette with colorCount colors
                        ushort colorShift = data.ReadUInt16();
                        ushort colorCount = data.ReadUInt16();

                        if (colorCount > 256 || colorShift > 255)
                        {
                            throw new Exception("Invalid internal palette");
                        }
                        else
                        {
                            uint[] buffer = new uint[colorCount];

                            for (int i = 0; i < colorCount; ++i)
                            {
                                // read in ARGB [0, 63] and save as ARGB [0, 255]
                                uint a = data.ReadByte();
                                uint r = (uint)data.ReadByte() << 2;
                                uint g = (uint)data.ReadByte() << 2;
                                uint b = (uint)data.ReadByte() << 2;

                                //if (a == 1)
                                //    a = 255;
                                //else if (a == 0)
                                a = 255;

                                uint color = b + (g << 8) + (r << 16) + (a << 24);
                                buffer[i] = color;
                            }

                            _internalPalette = new Palette();
                            _internalPalette.load(buffer, colorShift);
                            _updatePalettes();
                        }
                    }

                    // read in frame data
                    for (int f = 0; f < _frames.Length; ++f)
                    {
                        data.BaseStream.Position = _frames[f].offset;

                        // first byte of the "frame header" should be 1.
                        // if not, invalid frame! current frame's buffer remains null
                        if (data.ReadUInt16() != 1)
                        {
                            _frames[f].buffer = null;
                            _frames[f].bitmap = null;
                            continue;
                        }
                        else
                        {
                            //data.ReadUInt16();
                            //_frames[f].bitmap = new Bitmap(_header.width, _header.height);
                            _frames[f].buffer = new byte[_header.width * _header.height];
                            _frames[f].bitmap = new Bitmap(_header.width, _header.height);

                            short pixelCount, startY, xIndent, yIndent;
                            short cx = 0, cy = 0;

                            // get the starting Y position for drawing
                            startY = data.ReadInt16();
                            cy    += startY;

                            // draw loop until break (yIdent == 1000) or we go beyond the frame size (shouldn't happen normally?)
                            while ((data.BaseStream.Position - _frames[f].offset) < _frames[f].size)
                            {
                                pixelCount = data.ReadInt16();

                                // if the pixel count is > 0, this is a horizontal run
                                if (pixelCount > 0)
                                {
                                    xIndent = data.ReadInt16();
                                    cx     += xIndent;

                                    // copy the run of pixels
                                    data.Read(_frames[f].buffer, cx + cy * (short)_header.width, pixelCount);
                                    cx += pixelCount;

                                    // if the pixel count was odd, skip 1 extra byte!
                                    if ((pixelCount % 2) != 0)
                                    {
                                        data.ReadByte();
                                    }
                                }
                                // else, it's a y-indent with no drawing
                                else
                                {
                                    yIndent = data.ReadInt16();

                                    // if y-indent is 1000, it means quit?
                                    if (yIndent != 1000)
                                    {
                                        cy += yIndent;
                                        cx  = 0;
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    throw new Exception("image larger than 640x480 (?)");
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Loads a particular font (out of 6) from the given block.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="index"></param>
        public void load(LBXBlock block, int index)
        {
            if (block == null || index < 0 || index > 5)
            {
                throw new Exception("Invalid font load parameters.");
            }
            else if (block.data == null)
            {
                throw new Exception("Font block data is null");
            }
            else
            {
                // I love these things.
                BinaryReader data = new BinaryReader(new MemoryStream(block.data));

                // jump to the start of font[index]'s glyph widths;
                // 256 glyphs, 1 byte per width, total of 0x100 bytes per font
                data.BaseStream.Position = 0x59C + (long)index * 0x100;

                byte[] glyphWidths = new byte[256];
                this._maxGlyphWidth = 0;

                // read the widths
                for (int i = 0; i < 256; ++i)
                {
                    glyphWidths[i] = data.ReadByte();

                    if (glyphWidths[i] > this._maxGlyphWidth)
                    {
                        this._maxGlyphWidth = glyphWidths[i];
                    }
                }

                // jump to the start of font[index]'s glyph offsets;
                // 256 glyphs, 4 bytes per offset, total of 0x400 bytes per font
                data.BaseStream.Position = 0xB9C + (long)index * 0x400;

                uint[] glyphOffsets  = new uint[256];
                uint[] glyphDataSize = new uint[256];

                // read the offsets
                for (int i = 0; i < 256; ++i)
                {
                    glyphOffsets[i] = data.ReadUInt32();

                    // compute the data size as the difference between offsets
                    if (i > 0)
                    {
                        glyphDataSize[i - 1] = glyphOffsets[i] - glyphOffsets[i - 1];
                    }
                }

                // don't forget the last glyphDataSize
                //glyphDataSize[255] = data.ReadUInt32() - glyphOffsets[255];
                glyphDataSize[255] = 0;

                // the max glyph width is used to generate the temporary buffer into which
                // font data is decoded.  it's assumed no glyph would be taller than twice
                // the max width, but this isn't necessarily so.
                byte[,] glyphBuffer = new byte[maxGlyphWidth, maxGlyphWidth * 2];

                int cx, cy;
                int maxW, maxH;

                // jagged array of 2D arrays, mmmm.
                this._glyphData = new byte[256][, ];
                // this will track the distinct palette indices that this font uses
                this._glyphColors = new byte[256];

                this._maxGlyphHeight = 0;

                // read in the font data!
                for (int i = 0; i < 256; ++i)
                {
                    // all 6 fonts' offsets are relative to the start of the font data at 0x239C
                    data.BaseStream.Position = 0x239C + glyphOffsets[i];

                    cx   = 0;
                    cy   = 0;
                    maxW = 0;
                    maxH = 0;
                    //Array.Clear(glyphBuffer, 0, glyphBuffer.Length);
                    for (int y = 0; y < glyphBuffer.GetLength(1); ++y)
                    {
                        for (int x = 0; x < glyphBuffer.GetLength(0); ++x)
                        {
                            glyphBuffer[x, y] = 0xFF;
                        }
                    }

                    try
                    {
                        while ((data.BaseStream.Position - 0x239C - glyphOffsets[i]) < glyphDataSize[i])
                        {
                            byte val = data.ReadByte();

                            // 0x00-0x7f: color of next pixel
                            if (val <= 0x7F)
                            {
                                if (this._glyphColors[val] == 0)
                                {
                                    this._glyphColors[val]++;
                                }

                                if (cx >= 0 && cx < glyphBuffer.GetLength(0) &&
                                    cy >= 0 && cy < glyphBuffer.GetLength(1))
                                {
                                    glyphBuffer[cx, cy] = val;

                                    // track the total pixel-drawing extent of this glyph
                                    if (cx > maxW)
                                    {
                                        maxW = cx;
                                    }
                                    if (cy > maxH)
                                    {
                                        maxH = cy;
                                    }

                                    // increment after tracking!
                                    cx++;
                                }
                            }
                            // 0x80: skip to beginning of next line
                            else if (val == 0x80)
                            {
                                cx = 0;
                                cy++;
                            }
                            // 0x8n: skip n pixels
                            else
                            {
                                cx += (val & 0x0F);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        //
                    }

                    // now allocate the properly sized mini-buffer for this glyph and copy from the temp buffer
                    if (true)//maxW > 0 && maxH > 0)
                    {
                        this._glyphData[i] = new byte[maxW + 1, maxH + 1];

                        for (int x = 0; x < (maxW + 1); ++x)
                        {
                            for (int y = 0; y < (maxH + 1); ++y)
                            {
                                this._glyphData[i][x, y] = glyphBuffer[x, y];
                            }
                        }

                        // track the max glyph height
                        if ((maxH + 1) > this._maxGlyphHeight)
                        {
                            this._maxGlyphHeight = maxH + 1;
                        }
                    }
                }
            }
        }
Пример #8
0
        private void testButton_Click(object sender, EventArgs e)
        {
            LBXBlock block = new LBXBlock();

            // FONTS.LBX and IFONTS.LBX both contain palettes and fonts
            lbxReader.close();
            lbxReader.open(gameFolderPathTextBox.Text + "\\" + "FONTS.LBX");

            // load a palette to use
            lbxReader.read(block, 1);
            Palette pal = new Palette();

            // force the last palette entry to white
            block.data[0xFF * 4 + 0] = 0xFF;
            block.data[0xFF * 4 + 1] = 0x33;
            block.data[0xFF * 4 + 2] = 0x33;
            block.data[0xFF * 4 + 3] = 0x33;
            pal.load(block);

            lbxReader.close();
            lbxReader.open(gameFolderPathTextBox.Text + "\\" + "IFONTS.LBX");

            lbxReader.read(block, 0);
            Font font = new Font();

            // FONT THING
            font.load(block, 5);

            font.palette = pal;

            Bitmap   b  = new Bitmap(640, 480);
            Graphics gg = Graphics.FromImage(b);

            gg.Clear(Color.White);

            /*
             * for (int g = 32; g < 128; ++g)
             * {
             *  int x = (g % 16) * (font.maxGlyphWidth + 1) + 1;
             *  int y = (g / 16) * (font.maxGlyphHeight + 1) + 1;
             *  font.renderGlyph((byte)g, b, x, y);
             * }
             */

            font.renderString("Hello, Xus. :)", b, 10, 10);
            font.renderString(@"This seems to be working more or less? 2 + 2 = 4 (usually) \o/ :P", b, 10, 200);
            pictureBox1.Image = b;
            pictureBox1.Refresh();

            string colorList = "";

            for (int i = 0; i < 128; ++i)
            {
                if (font.glyphColors[i] > 0)
                {
                    colorList = colorList + i.ToString() + " ";
                }
            }
            fontColorsListTextBox.Text = colorList;

            lbxReader.close();
        }
Пример #9
0
        private void updateBlock()
        {
            LBXBlock block = new LBXBlock();

            // make sure an LBX file is loaded first (avoids crash on startup)
            if (currentLBXFileName.Length > 0)
            {
                // update block counter label
                currentLBXBlockLabel.Text = currentLBXBlock.ToString() + " / " + (lbxReader.getBlockCount() - 1).ToString();
                lbxReader.read(block, currentLBXBlock);

                if (block.data != null)
                {
                    // load new block as an image
                    currentImage = new Image();
                    currentImage.setExternalPalette(palettes[currentPaletteIndex]);
                    currentImage.load(block);
                    currentImageFrame = 0;

                    // update image header display with whatever was read
                    imageHeaderWidthTextBox.Text      = String.Format("{0:X2} {1:X2} ({2})", currentImage.getWidth() & 0xFF, (currentImage.getWidth() & 0xFF00) >> 8, currentImage.getWidth());
                    imageHeaderHeightTextBox.Text     = String.Format("{0:X2} {1:X2} ({2})", currentImage.getHeight() & 0xFF, (currentImage.getHeight() & 0xFF00) >> 8, currentImage.getHeight());
                    imageHeaderZeroTextBox.Text       = String.Format("{0:X2} {1:X2}", currentImage.getHeader().zero & 0xFF, (currentImage.getHeader().zero & 0xFF00) >> 8);
                    imageHeaderFrameCountTextBox.Text = String.Format("{0:X2} {1:X2} ({2})", currentImage.getHeader().frameCount & 0xFF, (currentImage.getHeader().frameCount & 0xFF00) >> 8, currentImage.getHeader().frameCount);
                    imageHeaderFrameDelayTextBox.Text = String.Format("{0:X2} {1:X2} ({2})", currentImage.getHeader().frameDelay & 0xFF, (currentImage.getHeader().frameDelay & 0xFF00) >> 8, currentImage.getHeader().frameDelay);
                    imageHeaderFlagsTextBox.Text      = formatImageFlagsString(currentImage.getHeader().flags);

                    // if there's an internal palette currently locked, render with that
                    if (internalPaletteLockButton.Checked)
                    {
                        currentImage.render(lockedInternalPalette);
                    }
                    else
                    {
                        currentImage.render();
                        if (currentImage.getInternalPalette() != null)
                        {
                            internalPaletteLockButton.Enabled = true;
                        }
                        else
                        {
                            internalPaletteLockButton.Enabled = false;
                        }
                    }
                    updatePalettes();

                    // enable/disable the next/prev frame buttons as appropriate
                    if (currentImage.getHeader().frameCount <= 1)
                    {
                        prevImageFrameButton.Enabled = false;
                        nextImageFrameButton.Enabled = false;
                    }
                    else
                    {
                        prevImageFrameButton.Enabled = false;
                        nextImageFrameButton.Enabled = true;
                    }

                    // update the track bar with an appropriate number of zoom levels:
                    // computed as either log_2(640 / width) or log_2(480 / height) depending on which is greater;
                    // determine this via the aspect ratio
                    float ar = (float)currentImage.getWidth() / (float)currentImage.getHeight();
                    float range;

                    // if the image is tall and skinny, use the height
                    if (ar < (4.0f / 3.0f))
                    {
                        range = 480.0f / (float)currentImage.getHeight();
                    }
                    else
                    {
                        range = 640.0f / (float)currentImage.getWidth();
                    }

                    // add 1 tick mark for the stretch-to-fit option at the end
                    int tickCount = (int)Math.Floor(Math.Log(range, 2.0)) + 1;

                    // snap the old trackbar value to the new range
                    if (imageZoomTrackBar.Value > tickCount)
                    {
                        imageZoomTrackBar.Value = tickCount;
                    }

                    imageZoomTrackBar.Minimum       = 0; // start at 2^0 or 1x zoom
                    imageZoomTrackBar.Maximum       = tickCount;
                    imageZoomTrackBar.TickFrequency = 1;
                    //imageZoomTrackBar.Value = 0;
                    imageZoomTrackBar_ValueChanged(null, null);
                }
            }
        }