2019-01-20 07:27:46 +08:00
using CodeWalker.GameFiles ;
using CodeWalker.Properties ;
2017-09-21 18:33:05 +08:00
using FastColoredTextBoxNS ;
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
2019-01-20 07:27:46 +08:00
using System.Xml ;
2017-09-21 18:33:05 +08:00
namespace CodeWalker.Forms
{
public partial class XmlForm : Form
{
private string xml ;
public string Xml
{
get { return xml ; }
set
{
xml = value ;
UpdateTextBoxFromData ( ) ;
}
}
private string fileName ;
public string FileName
{
get { return fileName ; }
set
{
fileName = value ;
UpdateFormTitle ( ) ;
}
}
public string FilePath { get ; set ; }
private bool modified = false ;
private bool LoadingXml = false ;
private bool DelayHighlight = false ;
2019-01-20 07:27:46 +08:00
private ExploreForm exploreForm = null ;
public RpfFileEntry rpfFileEntry { get ; private set ; } = null ;
2017-09-21 18:33:05 +08:00
2019-01-20 07:27:46 +08:00
public XmlForm ( ExploreForm owner )
2017-09-21 18:33:05 +08:00
{
2019-01-20 07:27:46 +08:00
exploreForm = owner ;
2017-09-21 18:33:05 +08:00
InitializeComponent ( ) ;
}
2019-01-20 07:27:46 +08:00
public void LoadXml ( string filename , string filepath , string xml , RpfFileEntry e )
2017-09-21 18:33:05 +08:00
{
FileName = filename ;
FilePath = filepath ;
Xml = xml ;
2019-01-20 07:27:46 +08:00
rpfFileEntry = e ;
2017-09-21 18:33:05 +08:00
modified = false ;
}
private void UpdateFormTitle ( )
{
Text = fileName + " - XML Editor - CodeWalker by dexyfex" ;
}
private void UpdateTextBoxFromData ( )
{
LoadingXml = true ;
XmlTextBox . Text = "" ;
XmlTextBox . Language = Language . XML ;
DelayHighlight = false ;
if ( string . IsNullOrEmpty ( xml ) )
{
LoadingXml = false ;
return ;
}
//if (xml.Length > (1048576 * 5))
//{
// XmlTextBox.Language = Language.Custom;
// XmlTextBox.Text = "[XML size > 10MB - Not shown due to performance limitations - Please use an external viewer for this file.]";
// return;
//}
//else
if ( xml . Length > ( 1024 * 512 ) )
{
XmlTextBox . Language = Language . Custom ;
DelayHighlight = true ;
}
//else
//{
// XmlTextBox.Language = Language.XML;
//}
Cursor = Cursors . WaitCursor ;
XmlTextBox . Text = xml ;
//XmlTextBox.IsChanged = false;
XmlTextBox . ClearUndo ( ) ;
Cursor = Cursors . Default ;
LoadingXml = false ;
}
private bool CloseDocument ( )
{
if ( modified )
{
var res = MessageBox . Show ( "Do you want to save the current document before closing it?" , "Save before closing" , MessageBoxButtons . YesNoCancel ) ;
switch ( res )
{
case DialogResult . Yes :
SaveDocument ( ) ;
break ;
case DialogResult . Cancel :
return false ;
}
}
FilePath = "" ;
FileName = "" ;
Xml = "" ;
modified = false ;
return true ;
}
private void NewDocument ( )
{
if ( ! CloseDocument ( ) ) return ; //same thing really..
FileName = "New.xml" ;
}
private void OpenDocument ( )
{
if ( OpenFileDialog . ShowDialog ( ) ! = DialogResult . OK ) return ;
if ( ! CloseDocument ( ) ) return ;
var fn = OpenFileDialog . FileName ;
if ( ! File . Exists ( fn ) ) return ; //couldn't find file?
Xml = File . ReadAllText ( fn ) ;
modified = false ;
FilePath = fn ;
FileName = new FileInfo ( fn ) . Name ;
}
private void SaveDocument ( bool saveAs = false )
{
2019-01-20 07:27:46 +08:00
if ( saveAs = = false )
{
var doc = new XmlDocument ( ) ;
try
{
doc . LoadXml ( xml ) ;
}
catch ( Exception ex )
{
MessageBox . Show ( "There's something wrong with your XML document:\r\n" + ex . Message , "Unable to parse XML" ) ;
return ;
}
if ( SaveToRPF ( xml ) )
{
return ;
}
//if saving to RPF failed for whatever reason, fallback to saving the file in the filesystem.
saveAs = true ;
}
2017-09-21 18:33:05 +08:00
if ( string . IsNullOrEmpty ( FileName ) ) saveAs = true ;
if ( string . IsNullOrEmpty ( FilePath ) ) saveAs = true ;
2018-02-21 17:12:17 +08:00
else if ( ( FilePath . ToLowerInvariant ( ) . StartsWith ( GTAFolder . CurrentGTAFolder . ToLowerInvariant ( ) ) ) ) saveAs = true ;
2017-09-21 18:33:05 +08:00
if ( ! File . Exists ( FilePath ) ) saveAs = true ;
var fn = FilePath ;
if ( saveAs )
{
if ( ! string . IsNullOrEmpty ( fn ) )
{
var dir = new FileInfo ( fn ) . DirectoryName ;
if ( ! Directory . Exists ( dir ) ) dir = "" ;
SaveFileDialog . InitialDirectory = dir ;
}
SaveFileDialog . FileName = FileName ;
if ( SaveFileDialog . ShowDialog ( ) ! = DialogResult . OK ) return ;
fn = SaveFileDialog . FileName ;
}
File . WriteAllText ( fn , xml ) ;
modified = false ;
FilePath = fn ;
FileName = new FileInfo ( fn ) . Name ;
}
2019-01-20 07:27:46 +08:00
private bool SaveToRPF ( string txt )
{
if ( ! ( exploreForm ? . EditMode ? ? false ) ) return false ;
if ( rpfFileEntry ? . Parent = = null ) return false ;
byte [ ] data = null ;
data = Encoding . UTF8 . GetBytes ( txt ) ;
if ( data = = null )
{
MessageBox . Show ( "Unspecified error - data was null!" , "Cannot save XML file" ) ;
return false ;
}
if ( ! rpfFileEntry . Path . ToLowerInvariant ( ) . StartsWith ( "mods" ) )
{
if ( MessageBox . Show ( "This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!" , "WARNING: Are you sure about this?" , MessageBoxButtons . YesNo ) ! = DialogResult . Yes )
{
return false ; //that was a close one
}
}
try
{
if ( ! ( exploreForm ? . EnsureRpfValidEncryption ( rpfFileEntry . File ) ? ? false ) ) return false ;
var newentry = RpfFile . CreateFile ( rpfFileEntry . Parent , rpfFileEntry . Name , data ) ;
if ( newentry ! = rpfFileEntry )
{ }
rpfFileEntry = newentry ;
exploreForm ? . RefreshMainListViewInvoke ( ) ; //update the file details in explorer...
modified = false ;
StatusLabel . Text = "XML file saved successfully at " + DateTime . Now . ToString ( ) ;
return true ; //victory!
}
catch ( Exception ex )
{
MessageBox . Show ( "Error saving file to RPF! The RPF archive may be corrupted...\r\n" + ex . ToString ( ) , "Really Bad Error" ) ;
}
return false ;
}
2017-09-21 18:33:05 +08:00
Style BlueStyle = new TextStyle ( Brushes . Blue , null , FontStyle . Regular ) ;
Style RedStyle = new TextStyle ( Brushes . Red , null , FontStyle . Regular ) ;
Style MaroonStyle = new TextStyle ( Brushes . Maroon , null , FontStyle . Regular ) ;
private void HTMLSyntaxHighlight ( Range range )
{
//clear style of changed range
range . ClearStyle ( BlueStyle , MaroonStyle , RedStyle ) ;
//tag brackets highlighting
range . SetStyle ( BlueStyle , @"<|/>|</|>" ) ;
//tag name
range . SetStyle ( MaroonStyle , @"<(?<range>[!\w]+)" ) ;
//end of tag
range . SetStyle ( MaroonStyle , @"</(?<range>\w+)>" ) ;
//attributes
range . SetStyle ( RedStyle , @"(?<range>\S+?)='[^']*'|(?<range>\S+)=""[^""]*""|(?<range>\S+)=\S+" ) ;
//attribute values
range . SetStyle ( BlueStyle , @"\S+?=(?<range>'[^']*')|\S+=(?<range>""[^""]*"")|\S+=(?<range>\S+)" ) ;
}
private void XmlTextBox_VisibleRangeChangedDelayed ( object sender , EventArgs e )
{
//this approach is much faster to load, but no outlining is available
//highlight only visible area of text
if ( DelayHighlight )
{
HTMLSyntaxHighlight ( XmlTextBox . VisibleRange ) ;
}
}
private void XmlTextBox_TextChanged ( object sender , TextChangedEventArgs e )
{
if ( ! LoadingXml )
{
xml = XmlTextBox . Text ;
modified = true ;
}
}
private void NewButton_ButtonClick ( object sender , EventArgs e )
{
NewDocument ( ) ;
}
private void OpenButton_ButtonClick ( object sender , EventArgs e )
{
OpenDocument ( ) ;
}
private void SaveButton_ButtonClick ( object sender , EventArgs e )
{
SaveDocument ( ) ;
}
private void FileNewMenu_Click ( object sender , EventArgs e )
{
NewDocument ( ) ;
}
private void FileOpenMenu_Click ( object sender , EventArgs e )
{
OpenDocument ( ) ;
}
private void FileSaveMenu_Click ( object sender , EventArgs e )
{
SaveDocument ( ) ;
}
private void FileSaveAsMenu_Click ( object sender , EventArgs e )
{
SaveDocument ( true ) ;
}
private void FileCloseMenu_Click ( object sender , EventArgs e )
{
Close ( ) ;
}
private void XmlForm_FormClosing ( object sender , FormClosingEventArgs e )
{
e . Cancel = ! CloseDocument ( ) ;
}
}
}