Wiki Downtime / Change Your Passwords!
This notice may be cached—the current version can be found here.

User:Markavian/CMV file format

From Dwarf Fortress Wiki
Jump to: navigation, search

This page is related to the CMV File format. The code below is a C# class for reading CMV files recorded from Dwarf Fortress (Tested 2008-09-11). I can't think of a license to put on it, but feel free to make use of it for non-comercial community related software; for example, I'm working on a CMV Editor at the moment.

Code below is based on work by SL (ShadowLord) and gamecode for writing CMV files forwarded by ToadyOne.


using System;
using System.Collections.Generic;
using System.Text;

using System.IO;
using ICSharpCode.SharpZipLib;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;

namespace CMVData
{
    public class CMV
    {
        const int NUM_SOUNDCHANNELS = 64;

        string file;

        List<byte[]> frames;
        uint version;
        uint cols;
        uint rows;
        uint delayRate;
        int numSounds;
        byte[] soundNames;
        byte[] soundTimings;

        public CMV()
        {
            frames = new List<byte[]>();
        }

        public CMV(string filename)
        {
            file = filename;

            frames = new List<byte[]>();
            readCMV(file);
        }

        private void readCMV(string inFilename)
        {
            checkExtension(inFilename);

            FileStream stream = File.OpenRead(inFilename);

            frames = new List<byte[]>();

            byte[] intBytes = new byte[4];

            stream.Read(intBytes, 0, 4);
            version = BitConverter.ToUInt32(intBytes, 0);

            stream.Read(intBytes, 0, 4);
            cols = BitConverter.ToUInt32(intBytes, 0);

            stream.Read(intBytes, 0, 4);
            rows = BitConverter.ToUInt32(intBytes, 0);

            stream.Read(intBytes, 0, 4);
            delayRate = BitConverter.ToUInt32(intBytes, 0);

            // Sounds
            if (version == 10001)
            {
                stream.Read(intBytes, 0, 4);
                numSounds = BitConverter.ToInt32(intBytes, 0);

                soundNames = new byte[50 * numSounds];
                stream.Read(soundNames, 0, 50 * numSounds);

                soundTimings = new byte[200 * NUM_SOUNDCHANNELS]; // 0x3200
                stream.Read(soundTimings, 0, 200 * NUM_SOUNDCHANNELS);
            }

            int frameSize = (int)(cols * rows);
            frameSize += frameSize;

            byte[] previousFrame = new byte[frameSize];
            for (int frameByte = 0; frameByte < frameSize; frameByte++)
            {
                previousFrame[frameByte] = 0;
            }

            byte[] curFrame = new byte[frameSize];
            int framesDone = 0;
            int frameSizeLeft = frameSize;
            int frameSizeDone = 0;

            while (stream.Position < stream.Length)
            {
                if (stream.Read(intBytes, 0, 4) == 4)
                {
                    int compressedChunkSize = BitConverter.ToInt32(intBytes, 0);
                    byte[] buffer = new byte[compressedChunkSize];
                    stream.Read(buffer, 0, compressedChunkSize);

                    MemoryStream memoryStream = new MemoryStream(buffer);
                    InflaterInputStream zipZipInStream = new InflaterInputStream(memoryStream);

                    int bytesRead = 0;
                    do
                    {
                        bytesRead = zipZipInStream.Read(curFrame, frameSizeDone, frameSizeLeft);
                        frames.Add((byte[])curFrame.Clone());

                        if (bytesRead == frameSizeLeft)
                        {
                            /* 
                             * Valid variables at this point: 
                             * frameSize
                             * byte[frameSize] previousFrame
                             * byte[frameSize] curFrame
                             */

                            framesDone++;
                            Array.Copy(curFrame, previousFrame, frameSize);
                            frameSizeLeft = frameSize;
                            frameSizeDone = 0;
                        }
                        else
                        {
                            frameSizeDone += bytesRead;
                            frameSizeLeft -= bytesRead;
                        }
                    } while (bytesRead == frameSize);
                }
                else
                {
                    break;
                }
            }

            stream.Close();
            stream.Dispose();
        }

        private void checkExtension(string filename)
        {
            if (filename.EndsWith(".cmv"))
            {
                // that's good
            }
            else if (filename.EndsWith(".ccmv"))
            {
                // that's possibly good
            }
            else
            {
                throw new CMVException("The supplied file type extension is not supported.");
            }
        }

        /* Public methods */
        public byte[] CloneFrame(int frameNumber)
        {
            byte[] frame;
            int frameSize;

            frameSize = (int)(rows * cols) << 2;
            frame = new byte[frameSize];
            frames[frameNumber].CopyTo(frame, 0);

            return frame;
        }

        /* Public properties */
        public byte[] Frame(int frameNumber)
        {
            if (frameNumber >= 0 && frameNumber < frames.Count)
            {
                return frames[frameNumber];
            }
            throw new CMVException("Frame number (" + frameNumber + ") out of bounds, maximum count " + frames.Count);
        }

        public string FilePath
        {
            get { return file; }
        }

        public string Filename
        {
            get {
                string[] raw = file.Split('\\');
                return raw[raw.Length - 1];
            }
        }

        public uint Frames
        {
            get { return (uint)frames.Count; }
        }

        public uint Version
        {
            get { return version; }
        }

        public uint Columns
        {
            get { return cols; }
        }

        public uint Rows
        {
            get { return rows; }
        }

        public int Sounds
        {
            get { return numSounds; }
        }
    }
}

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox
Advertisement