RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Adding write support?

Adding write support?

While I am petrified that there are nothing but sticky posts on this forum, I thought I would pose the question to people who have worked on this SDK in the past:

Would it be terribly difficult to add support for writing to the UO files? There is much potential in the idea of sending players patches, instead of entire custom map files.
 
Re:

Perhaps if I present a scenario it'll get people more excited:

Instead of sending a MUL file for the player to apply, a patcher program could be sent to all the players.

Then, the host can send information for the patcher to apply, instead of the patched file (which would contain a lot of redundant information).

This would vastly reduce patching download sizes, why it is an awesome idea, and why you should care to answer me if you know stuff about this SDK. :D
 

Jeff

Lord
TyloQuicksilver;798048 said:
Perhaps if I present a scenario it'll get people more excited:

Instead of sending a MUL file for the player to apply, a patcher program could be sent to all the players.

Then, the host can send information for the patcher to apply, instead of the patched file (which would contain a lot of redundant information).

This would vastly reduce patching download sizes, why it is an awesome idea, and why you should care to answer me if you know stuff about this SDK. :D

ConnectUO (when it is fixed, but old versions did this) does this, it uses MUO and UOP even Verdata patch files which are just the information that needs patching... This kind of thing is easy to write, and perhaps could be added to the UltimaSDK if done properly. If Ryan can get online for 15 minutes and get me what I need to get connectuo back up and running, I might find the time to actually add the write support to this project. Till then, it is what it is. It isnt difficult to add write support by the way, infact im pretty sure the avg developer could figure it out... I did back when I was semi newbish...
 
Re:

My first inclination was to simply reverse the reading process. Sound like a fair route wrought with few perils to you, Jeff?

Appreciate the help,
Tylo
 

Jeff

Lord
TyloQuicksilver;798157 said:
My first inclination was to simply reverse the reading process. Sound like a fair route wrought with few perils to you, Jeff?

Appreciate the help,
Tylo

Ya, not really. When you write data, if it is bigger than what already exists, you have to shift all the other data after it... kind of a pain in the ass..
 
Oh, yeah, that's a good point. I've heard about this MUO acronym before. It has something to do with UOGateway, right? Is there somewhere to go to get information about how it works?
 

Jeff

Lord
Here is some of the code I use in ConnectUO. Alot of it is sloppy and needs to be re-done... so I am not going to be posting it all but perhaps you can use some of this to help you along...

Basically of the 3 types of patching, 2 of them are documented; MUO and Verdata. UOP on the otherhand was not (at least not anywhere I could find at the time). So, I spent a day or two messing with it till I figured it out. It was basically like the MUO file with some slight differences. So, now that I'm posting this, it will be documented :)

FileID.cs
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace ConnectUO.Framework.Patching
{
    /// <summary>
    /// File ID's used by Krrios' MUO Application and Verdata.mul
    /// </summary>
    public enum FileId : int
    {
        Map0_mul        = 0x00000000,
        StaIdx0_mul     = 0x00000001,
        Statics0_mul    = 0x00000002,
        ArtIdx_mul      = 0x00000003,
        Art_mul         = 0x00000004,
        Anim_idx        = 0x00000005,
        Anim_mul        = 0x00000006,
        SoundIdx_mul    = 0x00000007,
        Sound_mul       = 0x00000008,
        TexIdx_mul      = 0x00000009,
        TexMaps_mul     = 0x0000000A,
        GumpIdx_mul     = 0x0000000B,
        GumpArt_mul     = 0x0000000C,
        Multi_idx       = 0x0000000D,
        Multi_mul       = 0x0000000E,
        Skills_idx      = 0x0000000F,
        Skills_mul      = 0x00000010,
        TileData_mul    = 0x00000011,
        AnimData_mul    = 0x00000012,
        Hues_mul        = 0x00000013,
        Anim2_idx       = 0x00020005,
        Anim2_mul       = 0x00020006,
        Anim3_idx       = 0x00030005,
        Anim3_mul       = 0x00030006,
        Anim4_idx       = 0x00040005,
        Anim4_mul       = 0x00040006,
        Anim5_idx       = 0x00050005,
        Anim5_mul       = 0x00050006,
    }
}

ExtendedFileID.cs
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace ConnectUO.Framework.Patching
{
    /// <summary>
    /// UOGateway's MUO creation tool unfortuantely didnt 
    /// follow the same File ID methodology as Krrios' MUO 
    /// Application... So thanks to UOGateway, this is a mess...
    /// </summary>
    public enum ExtendedFileId : int 
    {
        Map0_mul = 0x00000040,
        StaIdx0_mul = 0x00000041,
        Statics0_mul = 0x00000042,
        ArtIdx_mul = 0x00000043,
        Art_mul = 0x00000044,
        Anim_idx = 0x00000045,
        Anim_mul = 0x00000046,
        SoundIdx_mul = 0x00000047,
        Sound_mul = 0x00000048,
        TexIdx_mul = 0x00000049,
        TexMaps_mul = 0x0000004A,
        GumpIdx_mul = 0x0000004B,
        GumpArt_mul = 0x0000004C,
        Multi_idx = 0x0000004D,
        Multi_mul = 0x0000004E,
    }
}

Patch.cs
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace ConnectUO.Framework.Patching
{
    public struct Patch
    {
        public int FileId;
        public int BlockID;
        public int Extra;
        public int Length;
        public byte[] Data;
    }
}

PatchReader.cs
Code:
using System;
using System.Collections.Generic;
using System.IO;

namespace ConnectUO.Framework.Patching
{
    public enum PatchFileType
    {
        MUO,
        UOP,
        Verdata,
    }

	public class PatchReader : BinaryReader
	{
        public static PatchFileType ExtensionToPatchFileType(string ext)
        {
            ext = Path.GetExtension(ext);

            switch (ext)
            {
                case ".muo": return PatchFileType.MUO;
                case ".uop": return PatchFileType.UOP;
                case ".mul": return PatchFileType.Verdata;
                default: throw new Exception("Invalid file extension");
            }
        }

		public const int UOPHeader = 0x04504F55;
		public const int MUOHeader = 0x504f554d;

        private PatchFileType _patchFileType;

		public PatchReader(Stream stream, PatchFileType patchFileType) : base(stream)
		{
            _patchFileType = patchFileType;
        }

		public Patch ReadMUOPatch()
		{
			Patch p = new Patch();

			p.FileId = ReadInt32();
			p.BlockID = ReadInt32();
			p.Extra = ReadInt32();
			p.Length = ReadInt32();

			if (p.Length >= 0)
				p.Data = ReadBytes(p.Length);
			else
				p.Data = new byte[0];

			return p;
		}

		public Patch ReadUOPPatch()
		{
			Patch p = new Patch();
			p.FileId = (int)ReadByte();
			p.BlockID = ReadInt32();
			p.Length = ReadInt32();
			p.Extra = ReadInt32();
			p.Data = ReadBytes(p.Length);
			return p;
		}

		public Patch ReadVerdataPatch()
		{
			Patch p = new Patch();
			p.FileId = ReadInt32();
			p.BlockID = ReadInt32();

			int position = ReadInt32();

			p.Length = ReadInt32();
			p.Extra = ReadInt32();

			int streamPos = (int)BaseStream.Position;
			BaseStream.Seek(position, SeekOrigin.Begin);

			p.Data = ReadBytes(p.Length);

			BaseStream.Seek(streamPos, SeekOrigin.Begin);
			return p;
		}

		public string[] ReadMUOHeaderData()
		{
			ReadInt32();
			string[] headerInfo = new string[3];

			for( int i = 0; i < 3; i++ )
			{
				byte bChar;
				while( ( bChar = ReadByte() ) != 0 )
					headerInfo[i] += ( (char)bChar );
			}

			return headerInfo;
		}

		public List<Patch> ReadPatches()
		{
            List<Patch> patches = new List<Patch>();

            switch (_patchFileType)
            {
                case PatchFileType.MUO:
                    {
                        int header = ReadInt32();

                        if (header != MUOHeader)
                            throw new Exception("The current file is not a valid MUO file");

						string[] headerData = ReadMUOHeaderData();
                        int count = ReadInt32();

                        for (int i = 0; i < count; i++)
                            patches.Add(ReadMUOPatch());

                        break;
                    }
                case PatchFileType.UOP:
                    {
                       int header = ReadInt32();

                        if (header != UOPHeader)
                            throw new Exception("The current file is not a valid UOP file");

                        int count = ReadInt32();
                        int unknown = ReadInt32();

                        for (int i = 0; i < count; i++)
                            patches.Add(ReadUOPPatch());

                        break;
                    }
                case PatchFileType.Verdata:
                    {
                        int count = ReadInt32();

                        for (int i = 0; i < count; i++)
                            patches.Add(ReadVerdataPatch());

                        break;
                    }
            }

			return patches;
		}
	}
}

PatchWriter.cs
Code:
using System;
using System.Collections.Generic;
using System.IO;

namespace ConnectUO.Framework.Patching
{
    public class PatchWriter : BinaryWriter
    {
        public static void CreateMUO(string path, Patch[] patches)
        {
            PatchWriter writer = new PatchWriter(File.Create(path), PatchFileType.MUO);

            writer.WriteMUOHeader();
            writer.WriteMUOMetaData(new string[] { "MUO", "Created with ConnectUO", "Jeff Boulanger" });
            writer.Write((int)patches.Length);

            for (int i = 0; i < patches.Length; i++)
                writer.WriteMUOPatch(patches[i]);

            writer.Close();
        }
        public static void CreateUOP(string path, Patch[] patches)
        {
            PatchWriter writer = new PatchWriter(File.Create(path), PatchFileType.UOP);

            writer.WriteUOPHeader();
            writer.Write(patches.Length);
            writer.Write((int)0);//Unknown

            for (int i = 0; i < patches.Length; i++)
                writer.WriteUOPPatch(patches[i]);

            writer.Close();
        }

        private PatchFileType patchFileType;

        public PatchWriter(Stream stream, PatchFileType patchFileType)
            : base(stream)
        {
            if (patchFileType == PatchFileType.Verdata)
                throw new Exception("This file format is not supported");

            this.patchFileType = patchFileType;
        }

        public void WriteMUOMetaData(string[] metaData)
        {
            for (int i = 0; i < metaData.Length; i++)
            {
                char[] chars = metaData[i].ToCharArray();

                for (int c = 0; c < chars.Length; c++)
                    Write(chars[c]);

                Write((byte)0);
            }
        }

        public void WriteMUOHeader()
        {
            Write(PatchReader.MUOHeader);
        }

        public void WriteUOPHeader()
        {
            Write(PatchReader.UOPHeader);
        }

        public void WritePatch(Patch patch)
        {
            switch (patchFileType)
            {
                case PatchFileType.MUO:
                    {
                        WriteMUOPatch(patch);
                        break;
                    }
                case PatchFileType.UOP:
                    {
                        WriteUOPPatch(patch);
                        break;
                    }
            }
        }

        public void WriteMUOPatch(Patch patch)
        {
            Write((int)patch.FileId);
            Write((int)patch.BlockID);
            Write((int)patch.Extra);
            Write((int)patch.Length);
            Write(patch.Data);
        }

        public void WriteUOPPatch(Patch patch)
        {
            Write((byte)patch.FileId);
            Write((int)patch.BlockID);
            Write((int)patch.Length);
            Write((int)patch.Extra);
            Write(patch.Data);
        }
    }
}

Bleh, the formatting isn't working right. It looks fine in Visual Studio... dunno whats going on. Oh well... Enjoy.

I will help you with the patching of the actual MUL and IDX files if you need it, but try it yourself first now that you have the formats and reader/writer classes for the patch files themselves. Also, fair warning.. I never got around to testing the PatchWriter class. I wrote it, but never needed to use it since all I ever did was read patches.
 
Top