/// <summary> /// Extracts the texts and other data inside the codec.dat file from MGS. /// </summary> /// <param name="Data">The full path to the file with the data to be extracted</param> /// <param name="OutputFolder">The output folder where the extracted data will be placed</param> public static void Extract(string Data, string OutputFolder, MGSGame Game) { using (MemoryStream Input = new MemoryStream(File.ReadAllBytes(Data))) { Extract(Input, OutputFolder, Game); } }
/// <summary> /// Creates a codec.dat file from a folder with the extracted data. /// </summary> /// <param name="Data">The full path to the output file where the newly created data will be placed</param> /// <param name="InputFolder">The input folder with the extracted data</param> public static void Create(string Data, string InputFolder, MGSGame Game) { using (FileStream Output = new FileStream(Data, FileMode.Create)) { Create(Output, InputFolder, Game); } }
/// <summary> /// Extracts the subtitles from a movie.dat. /// </summary> /// <param name="Movie">The movie.dat file path</param> /// <param name="Output">The output folder</param> /// <param name="Game">The game being tampered (MGS3 or MGS4)</param> private static void Extract(string Movie, string Output, MGSGame Game) { Directory.CreateDirectory(Output); MGSText.Initialize(); using (FileStream Input = new FileStream(Movie, FileMode.Open)) { MovieSubtitle Out = new MovieSubtitle(); EndianBinaryReader Reader = null; switch (Game) { case MGSGame.MGSTS: Reader = new EndianBinaryReader(Input, Endian.Big); Game = MGSGame.MGS3; break; case MGSGame.MGS3: Reader = new EndianBinaryReader(Input, Endian.Little); break; case MGSGame.MGS4: Reader = new EndianBinaryReader(Input, Endian.Big); break; } int Index = 0; while (Input.Position < Input.Length) { StreamPacket Packet = StreamPacket.FromStream(Reader, Game); switch (Packet.Type) { case PacketType.Subtitle: Out.Subtitles.Add((SubtitlePacket)Packet); break; case PacketType.EndOfStream: string XmlName = string.Format("Subtitle_{0:D5}.xml", Index++); string FileName = Path.Combine(Output, XmlName); XmlSerializerNamespaces NameSpaces = new XmlSerializerNamespaces(); NameSpaces.Add(string.Empty, string.Empty); XmlWriterSettings Settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; XmlSerializer Serializer = new XmlSerializer(typeof(MovieSubtitle)); using (FileStream OutputStream = new FileStream(FileName, FileMode.Create)) { XmlWriter Writer = XmlWriter.Create(OutputStream, Settings); Serializer.Serialize(Writer, Out, NameSpaces); } Out.Subtitles.Clear(); break; } ReportProgress((float)Input.Position / Input.Length); } } }
/// <summary> /// Writes a Packet to a Stream. /// </summary> /// <param name="Writer">The writer of the output Stream</param> /// <param name="PacketText">The packet to be written</param> /// <param name="Game">The game being tampered</param> public static void ToStream(EndianBinaryWriter Writer, StreamPacket Packet, MGSGame Game) { switch (Packet.Type) { case PacketType.Subtitle: SubtitlePacket.ToStream(Writer, (SubtitlePacket)Packet, Game); break; case PacketType.EndOfStream: EndOfStreamPacket.ToStream(Writer, (EndOfStreamPacket)Packet); break; case PacketType.Raw: RawPacket.ToStream(Writer, (RawPacket)Packet); break; } }
public static string Buffer2Text(byte[] Data, MGSGame Game) { switch (Game) { case MGSGame.MGS3: StringBuilder Output = new StringBuilder(); for (int Index = 0; Index < Data.Length; Index++) { if (Data[Index] < 0x20 || Data[Index] > 0x7e) { if (Data[Index] == 0xa) { Output.Append(Environment.NewLine); } else if (Data[Index] == 0) { Output.Append("[end]"); } else { int Value = Data[Index]; if (Index + 1 < Data.Length) { Value = (Value << 8) | Data[++Index]; } if (Table[Value] == null) { Output.Append("\\x" + Value.ToString("X4")); } else { Output.Append(Table[Value]); } } } else { Output.Append((char)Data[Index]); } } return(Output.ToString()); case MGSGame.MGS4: return(MGS2Text(Encoding.UTF8.GetString(Data))); } return(null); }
/// <summary> /// Reads a Packet from a Stream. /// </summary> /// <param name="Reader">The reader of the Stream where the data is located</param> /// <param name="Game">The game being tampered</param> /// <returns>The packet as a object</returns> public static StreamPacket FromStream(EndianBinaryReader Reader, MGSGame Game) { uint StreamId = Reader.ReadUInt32() & 0xff; Reader.Seek(-4, SeekOrigin.Current); switch (StreamId) { case 4: return(SubtitlePacket.FromStream(Reader, Game)); case 0xf0: return(EndOfStreamPacket.FromStream(Reader)); default: return(RawPacket.FromStream(Reader)); } }
public static byte[] Text2Buffer(string Text, MGSGame Game) { switch (Game) { case MGSGame.MGS3: Text = Text2MGS(Text); using (MemoryStream Output = new MemoryStream()) { for (int Index = 0; Index < Text.Length; Index++) { if (Index < Text.Length - 1 && Text.Substring(Index, 2) == "\\x") { string Hex = Text.Substring(Index + 2, 4); ushort Value = ushort.Parse(Hex, NumberStyles.HexNumber); Output.WriteByte((byte)(Value >> 8)); Output.WriteByte((byte)Value); Index += 5; } else { bool InRange = Text[Index] < 0x20 || Text[Index] > 0x7e; if (InRange && Text[Index] != 0 && Text[Index] != 0xa) { int Value = Array.IndexOf(Table, Text.Substring(Index, 1)); if (Value > -1) { Output.WriteByte((byte)(Value >> 8)); Output.WriteByte((byte)Value); } } else { Output.WriteByte((byte)Text[Index]); } } } return(Output.ToArray()); } case MGSGame.MGS4: return(Encoding.UTF8.GetBytes(Text2MGS(Text))); } return(null); }
/// <summary> /// Reads a text entry of a Subtitle Packet from a Stream. /// </summary> /// <param name="Reader">The reader of the Stream where the data is located</param> /// <param name="Game">The game being tampered</param> /// <returns>The entry as a object</returns> public static SubtitlePacketText FromStream(EndianBinaryReader Reader, MGSGame Game) { SubtitlePacketText PacketText = new SubtitlePacketText(); PacketText.StartTime = Reader.ReadUInt32(); PacketText.EndTime = Reader.ReadUInt32(); uint Dummy = Reader.ReadUInt32(); ushort TextLength = Reader.ReadUInt16(); PacketText.LanguageId = Reader.ReadUInt16(); byte[] TextBuffer = new byte[TextLength - 0x10]; Reader.Read(TextBuffer, 0, TextBuffer.Length); PacketText.Text = MGSText.Buffer2Text(Unpad(TextBuffer), Game); PacketText.Text = PacketText.Text.Replace(Environment.NewLine, "\\n"); return(PacketText); }
/// <summary> /// Reads a Subtitle Packet from a Stream. /// </summary> /// <param name="Reader">The reader of the Stream where the data is located</param> /// <param name="Game">The game being tampered</param> /// <returns>The packet as a object</returns> public new static SubtitlePacket FromStream(EndianBinaryReader Reader, MGSGame Game) { SubtitlePacket Packet = new SubtitlePacket(); long BasePosition = Reader.BaseStream.Position; Packet.StreamId = Reader.ReadUInt32(); uint PacketLength = Reader.ReadUInt32(); long EndPosition = BasePosition + PacketLength; Packet.BaseStartTime = Reader.ReadUInt32(); uint Dummy = Reader.ReadUInt32(); uint DataLength = Reader.ReadUInt32(); while (Reader.BaseStream.Position + 0x10 < EndPosition) { Packet.Texts.Add(SubtitlePacketText.FromStream(Reader, Game)); } Reader.Seek(EndPosition, SeekOrigin.Begin); return(Packet); }
/// <summary> /// Inserts extracted subtitles into a movie.dat. /// </summary> /// <param name="Movie">The movie.dat file path</param> /// <param name="Input">The input folder with subtitles in XML</param> /// <param name="Game">The game being tampered (MGS3 or MGS4)</param> private static void Insert(string Movie, string Input, MGSGame Game) { string[] Files = Directory.GetFiles(Input); MGSText.Initialize(); string NewFile = Path.GetTempFileName(); FileStream In = new FileStream(Movie, FileMode.Open); FileStream Out = new FileStream(NewFile, FileMode.Create); Endian Endian = Endian.Default; switch (Game) { case MGSGame.MGSTS: Endian = Endian.Big; Game = MGSGame.MGS3; break; case MGSGame.MGS3: Endian = Endian.Little; break; case MGSGame.MGS4: Endian = Endian.Big; break; } EndianBinaryReader Reader = new EndianBinaryReader(In, Endian); EndianBinaryWriter Writer = new EndianBinaryWriter(Out, Endian); int Index = 0; int SubIndex = 0; MovieSubtitle Subtitle = GetSubtitle(Files[0]); while (In.Position < In.Length) { StreamPacket Packet = StreamPacket.FromStream(Reader, Game); switch (Packet.Type) { case PacketType.Subtitle: SubtitlePacket.ToStream(Writer, Subtitle.Subtitles[SubIndex++], Game); break; case PacketType.EndOfStream: if (++Index < Files.Length) { Subtitle = GetSubtitle(Files[Index]); } SubIndex = 0; break; } if (Packet.Type != PacketType.Subtitle) { StreamPacket.ToStream(Writer, Packet, Game); } ReportProgress((float)In.Position / In.Length); } In.Close(); Out.Close(); File.Delete(Movie); File.Move(NewFile, Movie); File.Delete(NewFile); }
/// <summary> /// Writes a text entry of a Subtitle Packet to a Stream. /// </summary> /// <param name="Writer">The writer of the output Stream</param> /// <param name="PacketText">The text entry to be written</param> /// <param name="Game">The game being tampered</param> public static void ToStream(EndianBinaryWriter Writer, SubtitlePacketText PacketText, MGSGame Game) { byte[] TextBuffer = new byte[0]; if (PacketText.Text != null) { PacketText.Text = PacketText.Text.Replace("\\n", Environment.NewLine); TextBuffer = MGSText.Text2Buffer(PacketText.Text, Game); } int Length = TextBuffer.Length + 1; if ((Length & 3) != 0) { Length = (Length & ~3) + 4; } Writer.Write(PacketText.StartTime); Writer.Write(PacketText.EndTime); Writer.Write(0u); Writer.Write((ushort)(Length + 0x10)); Writer.Write((ushort)PacketText.LanguageId); Writer.Write(TextBuffer); Writer.Write((byte)0); while ((Writer.BaseStream.Position & 3) != 0) { Writer.Write((byte)0); } }
/// <summary> /// Writes a Subtitle Packet to a Stream. /// </summary> /// <param name="Writer">The writer of the output Stream</param> /// <param name="PacketText">The packet to be written</param> /// <param name="Game">The game being tampered</param> public static void ToStream(EndianBinaryWriter Writer, SubtitlePacket Packet, MGSGame Game) { using (MemoryStream Content = new MemoryStream()) { EndianBinaryWriter CWriter = new EndianBinaryWriter(Content, Writer.Endian); foreach (SubtitlePacketText Text in Packet.Texts) { SubtitlePacketText.ToStream(CWriter, Text, Game); } int Length = (int)Content.Length + 0x14 + 1; if ((Length & 0xf) != 0) { Length = (Length & ~0xf) + 0x10; } Writer.Write(Packet.StreamId); Writer.Write(Length); Writer.Write(Packet.BaseStartTime); Writer.Write(0u); Writer.Write((uint)Content.Length); Writer.Write(Content.ToArray()); Writer.Write((byte)0); while ((Writer.BaseStream.Position & 0xf) != 0) { Writer.Write((byte)0); } } }
/// <summary> /// Extracts the texts and other data inside the codec.dat file from MGS. /// </summary> /// <param name="Data">The Stream with the data to be extracted</param> /// <param name="OutputFolder">The output folder where the extracted data will be placed</param> public static void Extract(Stream Data, string OutputFolder, MGSGame Game) { if (Game == MGSGame.MGS3) { MGSText.Initialize(); } BinaryReader Reader = new BinaryReader(Data); List <uint> AddressTable = new List <uint>(); uint Delimiter = Reader.ReadUInt32(); Data.Seek(0, SeekOrigin.Begin); while (Data.Position < Data.Length) { uint Position = (uint)Data.Position; uint Value = Reader.ReadUInt32(); if (Value == Delimiter) { AddressTable.Add(Position); } } AddressTable.Add((uint)Data.Length); for (int Index = 0; Index < AddressTable.Count - 1; Index++) { string OutputPath = Path.Combine(OutputFolder, string.Format("Text_{0:D5}", Index)); if (!Directory.Exists(OutputPath)) { Directory.CreateDirectory(OutputPath); } uint HeaderLength = 0; Data.Seek(AddressTable[Index], SeekOrigin.Begin); while (Reader.ReadInt32() != -1) { HeaderLength += 4; } uint BaseOffset = (uint)Data.Position; uint ScriptOffset = Reader.ReadUInt32() + BaseOffset; uint DialogsTableOffset = Reader.ReadUInt32() + BaseOffset; uint DialogsTextOffset = Reader.ReadUInt32() + BaseOffset; uint ScriptHeaderOffset = Reader.ReadUInt32() + BaseOffset; uint Key = Reader.ReadUInt32(); //Read header (with script pointers) Data.Seek(AddressTable[Index], SeekOrigin.Begin); byte[] Header = new byte[HeaderLength]; Reader.Read(Header, 0, Header.Length); MGSCrypto Crypto = new MGSCrypto(Key); StringBuilder Text = new StringBuilder(); //Read texts if (DialogsTableOffset != DialogsTextOffset) { int TextIndex = 0; bool HasText = true; while (HasText) { Data.Seek(DialogsTableOffset + TextIndex++ *4, SeekOrigin.Begin); uint Offset = (Reader.ReadUInt32() & 0x7fffffff) + DialogsTextOffset; uint NextOffset = (Reader.ReadUInt32() & 0x7fffffff) + DialogsTextOffset; if (Data.Position > DialogsTextOffset) { NextOffset = ScriptHeaderOffset; HasText = false; } Data.Seek(Offset, SeekOrigin.Begin); byte[] TextBuffer = new byte[NextOffset - Offset]; Reader.Read(TextBuffer, 0, TextBuffer.Length); TextBuffer = UnpadText(Crypto.ProcessBuffer(TextBuffer)); Text.Append(MGSText.Buffer2Text(TextBuffer, Game)); Text.Append(TextSeparator); } } if (Game == MGSGame.MGS3) { //This is probably not a header, but I don't know what this data is, so... string ScriptHeaderFile = Path.Combine(OutputPath, "ScriptHeader.bin"); Data.Seek(ScriptHeaderOffset, SeekOrigin.Begin); uint ScriptHeaderLength = ScriptOffset - ScriptHeaderOffset; byte[] ScriptHeader = new byte[ScriptHeaderLength]; Reader.Read(ScriptHeader, 0, ScriptHeader.Length); File.WriteAllBytes(ScriptHeaderFile, ScriptHeader); } //Read script Data.Seek(ScriptOffset, SeekOrigin.Begin); uint ScriptLength = AddressTable[Index + 1] - ScriptOffset; byte[] Script = new byte[ScriptLength]; Reader.Read(Script, 0, Script.Length); string HeaderFile = Path.Combine(OutputPath, "Header.bin"); string TextFile = Path.Combine(OutputPath, "Text.txt"); string ScriptFile = Path.Combine(OutputPath, "Script.bin"); File.WriteAllBytes(HeaderFile, Header); File.WriteAllText(TextFile, Text.ToString()); File.WriteAllBytes(ScriptFile, Script); if (OnStatusChanged != null) { float Percentage = (float)(Index + 1) / (AddressTable.Count - 1); OnStatusChanged(Percentage); } } }
/// <summary> /// Creates a codec.dat file from a folder with the extracted data. /// </summary> /// <param name="Data">The output stream where the newly created data will be placed</param> /// <param name="InputFolder">The input folder with the extracted data</param> public static void Create(Stream Data, string InputFolder, MGSGame Game) { if (Game == MGSGame.MGS3) { MGSText.Initialize(); } BinaryWriter Writer = new BinaryWriter(Data); string[] Folders = Directory.GetDirectories(InputFolder); int CurrentFolder = 0; foreach (string Folder in Folders) { string HeaderFile = Path.Combine(Folder, "Header.bin"); string TextFile = Path.Combine(Folder, "Text.txt"); string ScriptFile = Path.Combine(Folder, "Script.bin"); if (File.Exists(HeaderFile) && File.Exists(TextFile) && File.Exists(ScriptFile)) { const uint Key = 0x4805d94d; MGSCrypto Crypto = new MGSCrypto(Key); Writer.Write(File.ReadAllBytes(HeaderFile)); Writer.Write(-1); byte[] ScriptHeader = null; uint ScriptHeaderLength = 0; if (Game == MGSGame.MGS3) { string ScriptHeaderFile = Path.Combine(Folder, "ScriptHeader.bin"); if (File.Exists(ScriptHeaderFile)) { ScriptHeader = File.ReadAllBytes(ScriptHeaderFile); ScriptHeaderLength = (uint)ScriptHeader.Length; } } using (MemoryStream TextBlock = new MemoryStream()) { BinaryWriter TextWriter = new BinaryWriter(TextBlock); string Text = File.ReadAllText(TextFile); string[] Texts = Text.Split(new string[] { TextSeparator }, StringSplitOptions.RemoveEmptyEntries); int BaseTextOffset = 0x14 + Texts.Length * 4; int TextOffset = 0; TextBlock.Seek(BaseTextOffset, SeekOrigin.Begin); for (int Index = 0; Index < Texts.Length; Index++) { TextBlock.Seek(0x14 + Index * 4, SeekOrigin.Begin); TextWriter.Write((uint)(TextOffset | 0x80000000)); TextBlock.Seek(BaseTextOffset + TextOffset, SeekOrigin.Begin); byte[] Buffer = MGSText.Text2Buffer(Texts[Index], Game); TextWriter.Write(Crypto.ProcessBuffer(Buffer)); TextOffset += Buffer.Length; } int PadIndex = 0; while ((TextBlock.Position & 3) != 0) { byte Encrypted = Crypto.ProcessByte((byte)Padding[PadIndex++]); TextBlock.WriteByte(Encrypted); } uint ScriptHeaderOffset = (uint)TextBlock.Position; uint ScriptOffset = ScriptHeaderOffset + ScriptHeaderLength; TextBlock.Seek(0, SeekOrigin.Begin); TextWriter.Write(ScriptOffset); TextWriter.Write(0x14); TextWriter.Write(BaseTextOffset); TextWriter.Write(ScriptHeaderOffset); TextWriter.Write(Key); Writer.Write(TextBlock.ToArray()); } if (ScriptHeaderLength > 0) { Writer.Write(ScriptHeader); } Writer.Write(File.ReadAllBytes(ScriptFile)); while ((Data.Position & 0xf) != 0) { Data.WriteByte(0); } } if (OnStatusChanged != null) { float Percentage = (float)++CurrentFolder / Folders.Length; OnStatusChanged(Percentage); } } }