v50 Steam/Premium information for editors
  • v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
  • Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.

User:Markavian/CMV file format

From Dwarf Fortress Wiki
Jump to navigation Jump to 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; }
        }
    }
}