Mes documents/Visual Studio 2005/Projects/TES4ModTranslator/TES4ModTranslator/Records.cs

Go to the documentation of this file.
00001 using System;
00002 using System.Collections.Generic;
00003 using System.IO;
00004 
00005 namespace TESPluginParser {
00006     //Use an event in subrecords to detect changes
00007     /*public enum RecordTypes { ACHR, ACRE, ACTI, ALCH, AMMO, ANIO, APPA, ARMO, BOOK, BSGN, CELL, CLAS, CLMT, CLOT,
00008                     CONT, CREA, CSTY, DIAL, DOOR, EFSH, ENCH, EYES, FACT, FLOR, FURN, GLOB, GMST, GRAS, HAIR, IDLE,
00009                     INFO, INGR, KEYM, LAND, LIGH, LSCR, LTEX, LVLC, LVLI, LVSP, MGEF, MISC, NONE, NPC_, PACK, PGRD,
00010                     QUST, RACE, REFR, REGN, ROAD, SBSP, SCPT, SGST, SKIL, SLGM, SNDG, SOUN, SPEL, STAT, TES4, TLOD,
00011                     TOFT, TREE, WATR, WEAP, WRLD, WTHR, }*/
00012 
00013     public delegate void dValueChange(BaseRecord br, long ChangedBy);
00014     public class TESParserException : Exception { public TESParserException(string msg) : base(msg) { } }
00015 
00016     public abstract class BaseRecord {
00017         public string Name;
00018         private long _Size;
00019 
00020         public long Size {
00021             get { return _Size; }
00022             protected set {
00023                 if(value==_Size) return;
00024                 long temp=_Size;
00025                 _Size=value;
00026                 if(OnSizeChange!=null) OnSizeChange(this, value-temp);
00027             }
00028         }
00029         
00030         public event dValueChange OnSizeChange;
00031         public abstract string GetDesc();
00032         public abstract void DeleteRecord(BaseRecord br);
00033         public abstract void AddRecord(BaseRecord br);
00034         public abstract List<string> GetIDs(bool lower);
00035         internal abstract void SaveData(BinaryWriter bw);
00036 
00037         protected static string ReadRecName(BinaryReader br) {
00038             byte[] b = new byte[4];
00039             br.Read(b, 0, 4);
00040             return ""+((char)b[0])+((char)b[1])+((char)b[2])+((char)b[3]);
00041         }
00042         protected static void WriteString(BinaryWriter bw,string s) {
00043             byte[] b = new byte[s.Length];
00044             for(int i=0;i<s.Length;i++) b[i]=(byte)s[i];
00045             bw.Write(b, 0, s.Length);
00046         }
00047     }
00048 
00049     public sealed class Plugin : BaseRecord {
00050         public readonly List<Rec> Records = new List<Rec>();
00051 
00052         internal void RecordSizeChanged(BaseRecord br, long change) {
00053             if((br is Rec)&&Records.Contains((Rec)br)) Size+=change;
00054         }
00055 
00056         public override void DeleteRecord(BaseRecord br) {
00057             Rec r = br as Rec;
00058             if(r==null) return;
00059             for(int i=0;i<Records.Count;i++) {
00060                 if(Records[i]==r) {
00061                     if(r is GroupRecord) {
00062                         Size-=r.Size;
00063                     } else {
00064                         Size-=r.Size+20;
00065                     }
00066                     Records.RemoveAt(i);
00067                     return;
00068                 }
00069             }
00070         }
00071         public override void AddRecord(BaseRecord br) {
00072             Rec r=br as Rec;
00073             if(r==null) throw new TESParserException("Record to add was not of the correct type."+
00074                 Environment.NewLine+"Plugins can only hold Groups or Records.");
00075             Records.Add(r);
00076             if(r is GroupRecord) {
00077                 Size+=r.Size;
00078             } else {
00079                 Size+=r.Size+20;
00080             }
00081         }
00082 
00083         private void LoadPlugin(BinaryReader br) {
00084             string s;
00085             
00086             s=ReadRecName(br);
00087             if(s!="TES4") throw new Exception("File is not a valid TES4 plugin");
00088             Records.Add(new Tes4Record(br));
00089             while(br.PeekChar()!=-1) {
00090                 Records.Add(Rec.New(br));
00091             }
00092             foreach(Rec r in Records) {
00093                 r.OnSizeChange+=RecordSizeChanged;
00094             }
00095         }
00096 
00097         public Plugin(string FilePath) {
00098             Name=Path.GetFileName(FilePath);
00099             FileInfo fi=new FileInfo(FilePath);
00100             Size=fi.Length;
00101             BinaryReader br=new BinaryReader(fi.OpenRead());
00102             try {
00103                 LoadPlugin(br);
00104             } finally {
00105                 br.Close();
00106             }
00107         }
00108         public Plugin() {
00109             Name="New plugin";
00110             Size=0;
00111         }
00112 
00113         public override string GetDesc() {
00114             return "[Oblivion plugin]"+Environment.NewLine+
00115                 "Filename: "+Name+Environment.NewLine+
00116                 "File size: "+Size+Environment.NewLine+
00117                 "Records: "+Records.Count;
00118         }
00119 
00120         public void Save(string FilePath) {
00121             if(File.Exists(FilePath)) File.Delete(FilePath);
00122             BinaryWriter bw=new BinaryWriter(File.OpenWrite(FilePath));
00123             try {
00124                 SaveData(bw);
00125                 Name=Path.GetFileName(FilePath);
00126             } finally {
00127                 bw.Close();
00128             }
00129         }
00130 
00131         internal override void SaveData(BinaryWriter bw) {
00132             foreach(Rec r in Records) r.SaveData(bw);
00133         }
00134 
00135         public override List<string> GetIDs(bool lower) {
00136             List<string> list= new List<string>();
00137             foreach(Rec r in Records) list.AddRange(r.GetIDs(lower));
00138             return list;
00139         }
00140 
00141         public string Merge(Plugin p, bool MergeLists) {
00142             string result="";
00143             //strip out all group records
00144             for(int i=0;i<Records.Count;i++) {
00145                 if(Records[i] is GroupRecord) {
00146                     GroupRecord gr=(GroupRecord)Records[i--];
00147                     foreach(Record r in gr.Records) {
00148                         AddRecord(r);
00149                     }
00150                     DeleteRecord(gr);
00151                 }
00152             }
00153             //Get a list of records contained in the plugin
00154             List<string> ids=GetIDs(true);
00155             //create a list of all Records that need to be added
00156             List<Record> toadd=new List<Record>();
00157             foreach(Rec r in p.Records) {
00158                 if(r is Record) toadd.Add((Record)r);
00159                 else toadd.AddRange(((GroupRecord)r).Records);
00160             }
00161             //Cycle through each record, adding where needed
00162             foreach(Record r in toadd) {
00163                 if(r.Name=="TES4") continue;
00164                 List<string> id=r.GetIDs(true);
00165                 if(id.Count==0||!ids.Contains(id[0])) {
00166                     AddRecord(r);
00167                     continue;
00168                 }
00169                 /*if(MergeLists&&(r.Name=="LVLC"||r.Name=="LVLI")) {
00170                     Record thislist=null;
00171                     foreach(Record r2 in Records) {
00172                         List<string> id2=r2.GetIDs(true);
00173                         if(id2.Count==0||id2[0]==id[0]) {
00174                             thislist=r2;
00175                             break;
00176                         }
00177                     }
00178                     if(thislist==null) {
00179                         AddRecord(r);
00180                         continue;
00181                     }
00182                     result+="Leveled list "+id[0]+" was merged"+Environment.NewLine;
00183                     List<byte[]> entries=new List<byte[]>();
00184                     foreach(SubRecord sr in thislist.SubRecords) {
00185                         if(sr.Name!="LVLO") continue;
00186                         entries.Add(sr.GetData());
00187                     }
00188                     foreach(SubRecord sr in r.SubRecords) {
00189                         if(sr.Name!="LVLO") continue;
00190                         byte[] b=sr.GetData();
00191                         bool match=false;
00192                         foreach(byte[] b2 in entries) {
00193                             if(b.Length!=b2.Length) continue;
00194                             bool match2=true;
00195                             for(int i=0;i<b.Length;i++) {
00196                                 if(b[i]!=b2[i]) {
00197                                     match2=false;
00198                                     break;
00199                                 }
00200                             }
00201                             if(match2) {
00202                                 match=true;
00203                                 break;
00204                             }
00205                         }
00206                         if(!match) thislist.AddRecord(sr);
00207                     }
00208                     continue;
00209                 }*/
00210                 result+="Record "+id[0]+" of type "+r.Name+" could not be merged because it conflicted with an existing record."+Environment.NewLine;
00211             }
00212             return result;
00213         }
00214     }
00215 
00216     public abstract class Rec : BaseRecord {
00217         internal static Rec New(BinaryReader br) {
00218             string s=Plugin.ReadRecName(br);
00219             br.ReadUInt32();
00220             bool b=((br.ReadUInt32()&0x00040000)>0);
00221             br.BaseStream.Position-=8;
00222             return New(s, br, b);
00223         }
00224         internal static Rec New(string s, BinaryReader br) {
00225             br.ReadUInt32();
00226             bool b=((br.ReadUInt32()&0x00040000)>0);
00227             br.BaseStream.Position-=8;
00228             return New(s, br, b);
00229         }
00230         internal static Rec New(string s, BinaryReader br,bool compressed) {
00231             if(s=="GRUP") return new GroupRecord(br);
00232             else {
00233                 switch(s) {
00234                 case "TES4":
00235                     return new Tes4Record(br);
00236                 default:
00237                     if(compressed) return new CompressedRecord(s, br);
00238                     else return new Record(s, br);
00239                 }
00240             }
00241         }
00242     }
00243 
00244     public sealed class GroupRecord : Rec {
00245         public readonly List<Record> Records=new List<Record>();
00246         public readonly uint[] HeaderInfo=new uint[2];
00247 
00248         internal void RecordSizeChanged(BaseRecord br, long change) {
00249             if((br is Record)&&Records.Contains((Record)br)) Size+=change;
00250         }
00251 
00252         public override void DeleteRecord(BaseRecord br) {
00253             Record r = br as Record;
00254             if(r==null) return;
00255             for(int i=0;i<Records.Count;i++) {
00256                 if(Records[i]==r) {
00257                     Size-=r.Size+20;
00258                     Records.RemoveAt(i);
00259                     return;
00260                 }
00261             }
00262         }
00263         public override void AddRecord(BaseRecord br) {
00264             Record r=br as Record;
00265             if(r==null) throw new TESParserException("Record to add was not of the correct type."+
00266                 Environment.NewLine+"Groups can only hold Records.");
00267             Records.Add(r);
00268             Size+=r.Size+20;
00269         }
00270 
00271         internal GroupRecord(BinaryReader br) {
00272             Size=br.ReadUInt32();
00273             Name=Plugin.ReadRecName(br);
00274             for(int i=0;i<2;i++) HeaderInfo[i]=br.ReadUInt32();
00275             uint AmountRead=0;
00276             while(AmountRead<Size-20) {
00277                 string s=Plugin.ReadRecName(br);
00278                 if(s=="GRUP") {
00279                     for(int i=0;i<4;i++) br.ReadInt32();
00280                     Size-=20;
00281                     continue;
00282                 }
00283                 Record r=(Record)Rec.New(s, br);
00284                 AmountRead+=(uint)(r.Size+20);
00285                 Records.Add(r);
00286                 r.OnSizeChange+=RecordSizeChanged;
00287             }
00288         }
00289 
00290         public override string GetDesc() {
00291             return "[Record group]"+Environment.NewLine+
00292                 "Type: "+Name+Environment.NewLine+
00293                 "Records: "+Records.Count.ToString()+Environment.NewLine+
00294                 "Size: "+Size.ToString()+" bytes (including header)";
00295         }
00296 
00297         internal override void SaveData(BinaryWriter bw) {
00298             WriteString(bw, "GRUP");
00299             bw.Write((uint)Size);
00300             WriteString(bw,Name);
00301             for(int i=0;i<2;i++) bw.Write((uint)HeaderInfo[i]);
00302             foreach(Record r in Records) r.SaveData(bw);
00303         }
00304 
00305         public override List<string> GetIDs(bool lower) {
00306             List<string> list= new List<string>();
00307             foreach(Record r in Records) list.AddRange(r.GetIDs(lower));
00308             return list;
00309         }
00310     }
00311 
00312     public sealed class Tes4Record : Record {
00313         public readonly string Author;
00314         public readonly string Description;
00315 
00316         internal Tes4Record(BinaryReader br) : base("TES4",br) {
00317             foreach(SubRecord sr in SubRecords) {
00318                 switch(sr.Name) {
00319                 case "CNAM":
00320                     Author=sr.GetStrData();
00321                     break;
00322                 case "SNAM":
00323                     Description=sr.GetStrData();
00324                     break;
00325                 }
00326             }
00327         }
00328 
00329         public override string GetDesc() {
00330             return "[TES4 main header]"+Environment.NewLine+GetBaseDesc()+Environment.NewLine+Environment.NewLine+
00331                 "[Record specific data]"+Environment.NewLine+
00332                 "Mod author: "+Author+Environment.NewLine+
00333                 "Description: "+Description;
00334         }
00335     }
00336 
00337     public sealed class CompressedRecord : Record {
00338         byte[] CompressedData;
00339         uint UncompressedSize;
00340 
00341         internal CompressedRecord(string name,BinaryReader br) {
00342             Name=name;
00343             Size=br.ReadUInt32();
00344             for(int i=0;i<3;i++) HeaderInfo[i]=br.ReadUInt32();
00345             byte[] b=new byte[Size-4];
00346             byte[] output=new byte[br.ReadUInt32()];
00347             br.Read(b, 0, (int)(Size-4));
00348 
00349             ICSharpCode.SharpZipLib.Zip.Compression.Inflater inf;
00350             inf=new ICSharpCode.SharpZipLib.Zip.Compression.Inflater(false);
00351 
00352             inf.SetInput(b, 0, b.Length);
00353             inf.Inflate(output);
00354 
00355             CompressedData=b;
00356             UncompressedSize=(uint)output.Length;
00357 
00358             BinaryReader ubr=new BinaryReader(new MemoryStream(output));
00359             while(ubr.PeekChar()!=-1) {
00360                 string s=Plugin.ReadRecName(ubr);
00361                 if(s=="GRUP") {
00362                     for(int i=0;i<16;i++) ubr.ReadUInt32();
00363                     Size-=20;
00364                     continue;
00365                 }
00366                 SubRecord r=new SubRecord(s, ubr);
00367                 SubRecords.Add(r);
00368                 r.OnSizeChange+=CompressedSizeChanged;
00369             }
00370             ubr.Close();
00371         }
00372 
00373         public override void AddRecord(BaseRecord br) {
00374             SubRecord sr=br as SubRecord;
00375             if(sr==null) throw new TESParserException("Record to add was not of the correct type."+
00376                 Environment.NewLine+"Records can only hold Subrecords.");
00377             SubRecords.Add(sr);
00378             CompressedSizeChanged(null, 0);
00379         }
00380 
00381         public override void DeleteRecord(BaseRecord br) {
00382             SubRecord sr = br as SubRecord;
00383             if(sr==null) return;
00384             for(int i=0;i<SubRecords.Count;i++) {
00385                 if(SubRecords[i]==sr) {
00386                     SubRecords.RemoveAt(i);
00387                     break;
00388                 }
00389             }
00390             CompressedSizeChanged(null, 0);
00391         }
00392 
00393         public void CompressedSizeChanged(BaseRecord br, long ChangedBy) {
00394             //Create the deflater
00395             ICSharpCode.SharpZipLib.Zip.Compression.Deflater def;
00396             def=new ICSharpCode.SharpZipLib.Zip.Compression.Deflater(9);
00397             //Create and fill the memory stream
00398             MemoryStream ms=new MemoryStream();
00399             BinaryWriter bw=new BinaryWriter(ms);
00400             foreach(SubRecord sr in SubRecords) sr.SaveData(bw);
00401             bw.Flush();
00402             //compress the data
00403             byte[] input=new byte[ms.Length];
00404             ms.Seek(0, SeekOrigin.Begin);
00405             ms.Read(input, 0, input.Length);
00406             def.SetInput(input);
00407             UncompressedSize=(uint)input.Length;
00408             //Update the size and compressed data values
00409             def.Finish();
00410             byte[] compressed=new byte[Size*2];
00411             int compressedsize=0;
00412             while(true) {
00413                 int i=def.Deflate(compressed);
00414                 if(i==0) break;
00415                 compressedsize+=i;
00416             }
00417             Size=compressedsize+4;
00418             Array.Resize<byte>(ref compressed, compressedsize);
00419             CompressedData=compressed;
00420         }
00421 
00422         
00423 
00424         internal override void SaveData(BinaryWriter bw) {
00425             WriteString(bw, Name);
00426             bw.Write((uint)Size);
00427             for(int i=0;i<3;i++) bw.Write((uint)HeaderInfo[i]);
00428             bw.Write((uint)UncompressedSize);
00429             bw.Write(CompressedData);
00430         }
00431     }
00432 
00433     public class Record : Rec {
00434         public readonly List<SubRecord> SubRecords=new List<SubRecord>();
00435         public readonly uint[] HeaderInfo=new uint[3];
00436 
00437         internal void RecordSizeChanged(BaseRecord br, long change) {
00438             if((br is SubRecord)&&SubRecords.Contains((SubRecord)br)) Size+=change;
00439         }
00440 
00441         public override void DeleteRecord(BaseRecord br) {
00442             SubRecord sr = br as SubRecord;
00443             if(sr==null) return;
00444             for(int i=0;i<SubRecords.Count;i++) {
00445                 if(SubRecords[i]==sr) {
00446                     Size-=sr.Size+6;
00447                     SubRecords.RemoveAt(i);
00448                     return;
00449                 }
00450             }
00451         }
00452         public override void AddRecord(BaseRecord br) {
00453             SubRecord sr=br as SubRecord;
00454             if(sr==null) throw new TESParserException("Record to add was not of the correct type."+
00455                 Environment.NewLine+"Records can only hold Subrecords.");
00456             SubRecords.Add(sr);
00457             Size+=sr.Size+6;
00458         }
00459 
00460         internal Record(string name, BinaryReader br) {
00461             Name=name;
00462             Size=br.ReadUInt32();
00463             for(int i=0;i<3;i++) HeaderInfo[i]=br.ReadUInt32();
00464             uint AmountRead=0;
00465             while(AmountRead<Size) {
00466                 string s=Plugin.ReadRecName(br);
00467                 if(s=="XXXX") {
00468                     br.ReadUInt16();
00469                     uint i=br.ReadUInt32();
00470                     br.BaseStream.Position+=i+6;
00471                     Size-=i+16;
00472                     continue;
00473                 }
00474                 SubRecord r=new SubRecord(s, br);
00475                 AmountRead+=(uint)(r.Size+6);
00476                 SubRecords.Add(r);
00477                 r.OnSizeChange+=RecordSizeChanged;
00478             }
00479         }
00480         protected Record() { }
00481 
00482         protected string GetBaseDesc() {
00483             return "Type: "+Name+Environment.NewLine+
00484                 "Subrecords: "+SubRecords.Count.ToString()+Environment.NewLine+
00485                 "Size: "+Size.ToString()+" bytes (excluding header)";
00486         }
00487 
00488         public override string GetDesc() {
00489             return "[Unknown record]"+Environment.NewLine+GetBaseDesc();
00490         }
00491 
00492         internal override void SaveData(BinaryWriter bw) {
00493             WriteString(bw, Name);
00494             bw.Write((uint)Size);
00495             for(int i=0;i<3;i++) bw.Write((uint)HeaderInfo[i]);
00496             foreach(SubRecord sr in SubRecords) sr.SaveData(bw);
00497         }
00498 
00499         public override List<string> GetIDs(bool lower) {
00500             List<string> list= new List<string>();
00501             foreach(SubRecord sr in SubRecords) list.AddRange(sr.GetIDs(lower));
00502             return list;
00503         }
00504     }
00505 
00506     public class SubRecord : BaseRecord {
00507         private byte[] Data;
00508 
00509         public byte[] GetData() {
00510             return (byte[])Data.Clone();
00511         }
00512         public void SetData(byte[] data) {
00513             Data=(byte[])data.Clone();
00514             Size=data.Length;
00515         }
00516 
00517         internal SubRecord(string name, BinaryReader br) {
00518             Name=name;
00519             Size=br.ReadUInt16();
00520             Data=new byte[Size];
00521             br.Read(Data, 0, Data.Length);
00522         }
00523 
00524         internal override void SaveData(BinaryWriter bw) {
00525             WriteString(bw, Name);
00526             bw.Write((ushort)Size);
00527             bw.Write(Data, 0, Data.Length);
00528         }
00529 
00530         public override string GetDesc() {
00531             return "[Unknown subrecord]"+Environment.NewLine+
00532                 "Name: "+Name+Environment.NewLine+
00533                 "Size: "+Size.ToString()+" bytes (Excluding header)";
00534         }
00535         public override void DeleteRecord(BaseRecord br) { }
00536         public override void AddRecord(BaseRecord br) {
00537             throw new TESParserException("Subrecords cannot contain additional data.");
00538         }
00539         public string GetStrData() {
00540             string s="";
00541             foreach(byte b in Data) { 
00542                 if(b==0) break;
00543                 s+=(char)b;
00544             }
00545             return s;
00546         }
00547         public string GetHexData() {
00548             string s="";
00549             foreach(byte b in Data) s+=b.ToString("X").PadLeft(2, '0')+" ";
00550             return s;
00551         }
00552         public override List<string> GetIDs(bool lower) {
00553             List<string> list= new List<string>();
00554             if(Name=="EDID") {
00555                 if(lower) {
00556                     list.Add(this.GetStrData().ToLower());
00557                 } else {
00558                     list.Add(this.GetStrData());
00559                 }
00560             }
00561             return list;
00562         }
00563     }
00564 }

Generated on Fri Jun 23 21:50:04 2006 for OblivionModTranslator by  doxygen 1.4.6-NO