2017-09-21 18:33:05 +08:00
using CodeWalker.Forms ;
using CodeWalker.GameFiles ;
using CodeWalker.Properties ;
using CodeWalker.World ;
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Diagnostics ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
2018-01-10 12:55:19 +08:00
using System.Xml ;
2017-09-21 18:33:05 +08:00
namespace CodeWalker
{
public partial class ExploreForm : Form
{
private volatile bool Ready = false ;
private Dictionary < string , FileTypeInfo > FileTypes ;
private MainTreeFolder RootFolder ;
private MainTreeFolder CurrentFolder ;
private List < MainListItem > CurrentFiles ;
private Stack < MainTreeFolder > BackSteps = new Stack < MainTreeFolder > ( ) ;
private Stack < MainTreeFolder > ForwardSteps = new Stack < MainTreeFolder > ( ) ;
private bool HistoryNavigating = false ;
private int SortColumnIndex = 0 ;
private SortOrder SortDirection = SortOrder . Ascending ;
private int PreviousPathColumnWidth = 0 ;
public volatile bool Searching = false ;
private MainTreeFolder SearchResults ;
private List < RpfFile > AllRpfs { get ; set ; }
private GameFileCache FileCache { get ; set ; } = new GameFileCache ( ) ;
private object FileCacheSyncRoot = new object ( ) ;
private bool EditMode = false ;
public ExploreForm ( )
{
InitializeComponent ( ) ;
ShowMainListViewPathColumn ( false ) ;
}
private void Init ( )
{
//called from ExploreForm_Load
InitFileTypes ( ) ;
2018-02-21 16:27:01 +08:00
// This is probably not necessary now that the GTA folder is checked
// in the Program.cs when the game is initiated, but we will leave it
// here for now to make sure
2018-02-18 09:26:35 +08:00
if ( ! GTAFolder . UpdateGTAFolder ( true ) )
2017-09-21 18:33:05 +08:00
{
Close ( ) ;
return ;
}
Task . Run ( ( ) = >
{
try
{
2018-02-18 17:20:39 +08:00
GTA5Keys . LoadFromPath ( GTAFolder . CurrentGTAFolder ) ;
2017-09-21 18:33:05 +08:00
}
catch
{
UpdateStatus ( "Unable to load gta5.exe!" ) ;
return ;
}
UpdateStatus ( "Scanning..." ) ;
RefreshMainTreeView ( ) ;
UpdateStatus ( "Scan complete." ) ;
InitFileCache ( ) ;
while ( ! IsDisposed ) //run the file cache content thread until the form exits.
{
if ( FileCache . IsInited )
{
2018-01-02 04:52:54 +08:00
bool fcItemsPending = FileCache . ContentThreadProc ( ) ;
2017-09-21 18:33:05 +08:00
2018-01-02 04:52:54 +08:00
if ( ! fcItemsPending )
2017-09-21 18:33:05 +08:00
{
Thread . Sleep ( 10 ) ;
}
}
else
{
Thread . Sleep ( 20 ) ;
}
}
} ) ;
}
private void InitFileCache ( )
{
Task . Run ( ( ) = >
{
lock ( FileCacheSyncRoot )
{
if ( ! FileCache . IsInited )
{
UpdateStatus ( "Loading file cache..." ) ;
var allRpfs = AllRpfs ;
FileCache . Init ( UpdateStatus , UpdateErrorLog , allRpfs ) ; //inits main dicts and archetypes only...
UpdateStatus ( "Loading materials..." ) ;
BoundsMaterialTypes . Init ( FileCache ) ;
UpdateStatus ( "Loading scenario types..." ) ;
Scenarios . EnsureScenarioTypes ( FileCache ) ;
UpdateStatus ( "File cache loaded." ) ;
}
}
} ) ;
}
public GameFileCache GetFileCache ( )
{
lock ( FileCacheSyncRoot )
{
if ( FileCache . IsInited ) return FileCache ;
}
InitFileCache ( ) ; //if we got here, it's not inited yet - init it!
return FileCache ; //return it even though it's probably not inited yet..
}
private void InitFileTypes ( )
{
FileTypes = new Dictionary < string , FileTypeInfo > ( ) ;
InitFileType ( ".rpf" , "Rage Package File" , 3 ) ;
InitFileType ( "" , "File" , 4 ) ;
InitFileType ( ".dat" , "Data File" , 4 ) ;
InitFileType ( ".cab" , "CAB File" , 4 ) ;
InitFileType ( ".txt" , "Text File" , 5 , FileTypeAction . ViewText ) ;
InitFileType ( ".gxt2" , "Global Text Table" , 5 , FileTypeAction . ViewGxt ) ;
InitFileType ( ".log" , "LOG File" , 5 , FileTypeAction . ViewText ) ;
InitFileType ( ".ini" , "Config Text" , 5 , FileTypeAction . ViewText ) ;
InitFileType ( ".vdf" , "Steam Script File" , 5 , FileTypeAction . ViewText ) ;
InitFileType ( ".sps" , "Shader Preset" , 5 , FileTypeAction . ViewText ) ;
InitFileType ( ".xml" , "XML File" , 6 , FileTypeAction . ViewXml ) ;
InitFileType ( ".meta" , "Metadata (XML)" , 6 , FileTypeAction . ViewXml ) ;
InitFileType ( ".ymt" , "Metadata (Binary)" , 6 , FileTypeAction . ViewYmt ) ;
InitFileType ( ".pso" , "Metadata (PSO)" , 6 , FileTypeAction . ViewJPso ) ;
InitFileType ( ".gfx" , "Scaleform Flash" , 7 ) ;
InitFileType ( ".ynd" , "Path Nodes" , 8 ) ;
InitFileType ( ".ynv" , "Nav Mesh" , 9 , FileTypeAction . ViewModel ) ;
InitFileType ( ".yvr" , "Vehicle Record" , 9 , FileTypeAction . ViewYvr ) ;
InitFileType ( ".ywr" , "Waypoint Record" , 9 , FileTypeAction . ViewYwr ) ;
InitFileType ( ".fxc" , "Compiled Shaders" , 9 , FileTypeAction . ViewFxc ) ;
InitFileType ( ".yed" , "Expression Dictionary" , 9 ) ;
InitFileType ( ".asi" , "ASI Plugin" , 9 ) ;
InitFileType ( ".dll" , "Dynamic Link Library" , 9 ) ;
InitFileType ( ".exe" , "Executable" , 10 ) ;
InitFileType ( ".yft" , "Fragment" , 11 , FileTypeAction . ViewModel ) ;
InitFileType ( ".ydr" , "Drawable" , 11 , FileTypeAction . ViewModel ) ;
InitFileType ( ".ydd" , "Drawable Dictionary" , 12 , FileTypeAction . ViewModel ) ;
InitFileType ( ".cut" , "Cutscene" , 12 , FileTypeAction . ViewCut ) ;
InitFileType ( ".ysc" , "Script" , 13 ) ;
InitFileType ( ".ymf" , "Manifest" , 14 , FileTypeAction . ViewYmf ) ;
InitFileType ( ".bik" , "Bink Video" , 15 ) ;
InitFileType ( ".jpg" , "JPEG Image" , 16 ) ;
InitFileType ( ".jpeg" , "JPEG Image" , 16 ) ;
InitFileType ( ".gif" , "GIF Image" , 16 ) ;
InitFileType ( ".png" , "Portable Network Graphics" , 16 ) ;
InitFileType ( ".dds" , "DirectDraw Surface" , 16 ) ;
InitFileType ( ".ytd" , "Texture Dictionary" , 16 , FileTypeAction . ViewYtd ) ;
InitFileType ( ".mrf" , "MRF File" , 18 ) ;
2017-09-29 20:23:37 +08:00
InitFileType ( ".ycd" , "Clip Dictionary" , 18 , FileTypeAction . ViewYcd ) ;
2017-09-21 18:33:05 +08:00
InitFileType ( ".ypt" , "Particle Effect" , 18 , FileTypeAction . ViewModel ) ;
InitFileType ( ".ybn" , "Static Collisions" , 19 , FileTypeAction . ViewModel ) ;
InitFileType ( ".ide" , "Item Definitions" , 20 , FileTypeAction . ViewText ) ;
InitFileType ( ".ytyp" , "Archetype Definitions" , 20 , FileTypeAction . ViewYtyp ) ;
InitFileType ( ".ymap" , "Map Data" , 21 , FileTypeAction . ViewYmap ) ;
InitFileType ( ".ipl" , "Item Placements" , 21 , FileTypeAction . ViewText ) ;
InitFileType ( ".awc" , "Audio Wave Container" , 22 , FileTypeAction . ViewAwc ) ;
InitFileType ( ".rel" , "Audio Data (REL)" , 23 , FileTypeAction . ViewRel ) ;
2017-12-14 18:49:41 +08:00
InitSubFileType ( ".dat" , "cache_y.dat" , "Cache File" , 6 , FileTypeAction . ViewCacheDat ) ;
2017-09-21 18:33:05 +08:00
}
private void InitFileType ( string ext , string name , int imgidx , FileTypeAction defaultAction = FileTypeAction . ViewHex )
{
var ft = new FileTypeInfo ( ext , name , imgidx , defaultAction ) ;
FileTypes [ ext ] = ft ;
}
2017-12-14 18:49:41 +08:00
private void InitSubFileType ( string ext , string subext , string name , int imgidx , FileTypeAction defaultAction = FileTypeAction . ViewHex )
{
FileTypeInfo pti = null ;
if ( FileTypes . TryGetValue ( ext , out pti ) )
{
var ft = new FileTypeInfo ( subext , name , imgidx , defaultAction ) ;
pti . AddSubType ( ft ) ;
}
}
2017-09-21 18:33:05 +08:00
public FileTypeInfo GetFileType ( string fn )
{
var fi = new FileInfo ( fn ) ;
2017-12-14 18:49:41 +08:00
var ext = fi . Extension . ToLowerInvariant ( ) ;
2017-09-21 18:33:05 +08:00
if ( ! string . IsNullOrEmpty ( ext ) )
{
FileTypeInfo ft ;
if ( FileTypes . TryGetValue ( ext , out ft ) )
{
2017-12-14 18:49:41 +08:00
if ( ft . SubTypes ! = null )
{
var fnl = fn . ToLowerInvariant ( ) ;
foreach ( var sft in ft . SubTypes )
{
if ( fnl . EndsWith ( sft . Extension ) )
{
return sft ;
}
}
}
2017-09-21 18:33:05 +08:00
return ft ;
}
else
{
ft = new FileTypeInfo ( ext , ext . Substring ( 1 ) . ToUpperInvariant ( ) + " File" , 4 , FileTypeAction . ViewHex ) ;
FileTypes [ ft . Extension ] = ft ; //save it for later!
return ft ;
}
}
else
{
return FileTypes [ "" ] ;
}
}
2018-02-18 17:20:39 +08:00
2017-09-21 18:33:05 +08:00
public void UpdateStatus ( string text )
{
try
{
if ( InvokeRequired )
{
BeginInvoke ( new Action ( ( ) = > { UpdateStatus ( text ) ; } ) ) ;
}
else
{
StatusLabel . Text = text ;
}
}
catch { }
}
public void UpdateErrorLog ( string text )
{
try
{
if ( InvokeRequired )
{
BeginInvoke ( new Action ( ( ) = > { UpdateErrorLog ( text ) ; } ) ) ;
}
else
{
//StatusLabel.Text = text;
}
}
catch { }
}
public void Navigate ( MainTreeFolder f )
{
if ( ! Ready ) return ;
if ( f = = CurrentFolder ) return ; //already there!
if ( f . IsSearchResults )
{
foreach ( TreeNode tn in MainTreeView . Nodes )
{
var tnf = tn . Tag as MainTreeFolder ;
if ( ( tnf ! = null ) & & ( tnf . IsSearchResults ) )
{
MainTreeView . Nodes . Remove ( tn ) ; //remove existing search result node
break ;
}
}
AddMainTreeViewRoot ( f ) ; //add the current search result node
TreeNode sr = FindTreeNode ( f , null ) ;
if ( sr ! = null )
{
MainTreeView . SelectedNode = sr ; //navigate to the new search results node
}
return ;
}
List < MainTreeFolder > hierarchy = new List < MainTreeFolder > ( ) ;
var pf = f ;
while ( pf ! = null )
{
hierarchy . Add ( pf ) ;
pf = pf . Parent ;
}
TreeNode n = null ;
for ( int i = hierarchy . Count - 1 ; i > = 0 ; i - - )
{
n = FindTreeNode ( hierarchy [ i ] , n ) ;
}
if ( n ! = null )
{
if ( MainTreeView . SelectedNode ! = n )
{
MainTreeView . SelectedNode = n ; //this will trigger everything else
}
}
else
{
Navigate ( f . Path ) ; //try again using the path string...
//NavigateFailed(f.Path); //unable to navigate...
}
}
public void Navigate ( string path )
{
if ( ! Ready ) return ;
var pathl = path . ToLowerInvariant ( ) . Replace ( '/' , '\\' ) ;
if ( ( CurrentFolder ! = null ) & & ( CurrentFolder . Path . ToLowerInvariant ( ) = = pathl ) ) return ; //already there
var hierarchy = pathl . Split ( new [ ] { '\\' } , StringSplitOptions . RemoveEmptyEntries ) ;
TreeNode n = MainTreeView . Nodes [ 0 ] ; // FindTreeNode("gta v", null);
if ( ! string . IsNullOrEmpty ( path ) )
{
for ( int i = 0 ; i < hierarchy . Length ; i + + )
{
n = FindTreeNode ( hierarchy [ i ] , n ) ;
}
}
if ( n ! = null )
{
if ( MainTreeView . SelectedNode ! = n )
{
MainTreeView . SelectedNode = n ; //this will trigger everything else
}
}
else
{
NavigateFailed ( path ) ; //unable to navigate...
}
}
private void NavigateFailed ( string path )
{
MessageBox . Show ( "Unable to navigate to \"" + path + "\"." ) ;
}
private void NavigateComplete ( MainTreeFolder prevFolder )
{
//called after the CurrentFolder and CurrentFiles have changed.
UpdateNavigateUI ( ) ;
2018-01-11 12:03:58 +08:00
EnsureEditModeWarning ( ) ;
2017-09-21 18:33:05 +08:00
if ( ! HistoryNavigating ) //only do this if not currently navigating forward or back
{
if ( prevFolder ! = null )
{
ForwardSteps . Clear ( ) ;
BackSteps . Push ( prevFolder ) ;
}
UpdateHistoryUI ( ) ;
}
GoButton . Enabled = true ;
RefreshButton . Enabled = true ;
SearchButton . Enabled = true ;
2018-01-10 11:17:30 +08:00
EditModeButton . Enabled = true ;
2017-09-21 18:33:05 +08:00
}
public void GoUp ( MainTreeFolder toFolder = null )
{
var fld = ( toFolder = = null ) ? CurrentFolder ? . Parent : toFolder ;
if ( fld = = null ) return ;
Navigate ( fld ) ;
}
public void GoBack ( MainTreeFolder toFolder = null )
{
if ( BackSteps . Count = = 0 ) return ;
var s = BackSteps . Pop ( ) ;
ForwardSteps . Push ( CurrentFolder ) ;
while ( ( toFolder ! = null ) & & ( s ! = toFolder ) & & ( BackSteps . Count > 0 ) )
{
ForwardSteps . Push ( s ) ;
s = BackSteps . Pop ( ) ;
}
HistoryNavigating = true ;
Navigate ( s ) ;
HistoryNavigating = false ;
UpdateHistoryUI ( ) ;
}
public void GoForward ( MainTreeFolder toFolder = null )
{
if ( ForwardSteps . Count = = 0 ) return ;
var s = ForwardSteps . Pop ( ) ;
BackSteps . Push ( CurrentFolder ) ;
while ( ( toFolder ! = null ) & & ( s ! = toFolder ) & & ( ForwardSteps . Count > 0 ) )
{
BackSteps . Push ( s ) ;
s = ForwardSteps . Pop ( ) ;
}
HistoryNavigating = true ;
Navigate ( s ) ;
HistoryNavigating = false ;
UpdateHistoryUI ( ) ;
}
private void UpdateNavigateUI ( )
{
UpButton . DropDownItems . Clear ( ) ;
var pf = CurrentFolder ? . Parent ;
int i = 0 ;
while ( pf ! = null )
{
var button = UpButton . DropDownItems . Add ( pf . GetToolText ( ) ) ;
button . Tag = pf ;
button . Click + = UpListButton_Click ;
pf = pf . Parent ;
if ( i > = 10 ) break ;
}
UpButton . Enabled = ( UpButton . DropDownItems . Count > 0 ) ;
}
private void UpdateHistoryUI ( )
{
BackButton . DropDownItems . Clear ( ) ;
ForwardButton . DropDownItems . Clear ( ) ;
int i = 0 ;
foreach ( var step in BackSteps )
{
var button = BackButton . DropDownItems . Add ( step . GetToolText ( ) ) ;
button . Tag = step ;
button . Click + = BackListButton_Click ;
i + + ;
if ( i > = 10 ) break ;
}
i = 0 ;
foreach ( var step in ForwardSteps )
{
var button = ForwardButton . DropDownItems . Add ( step . GetToolText ( ) ) ;
button . Tag = step ;
button . Click + = ForwardListButton_Click ;
i + + ;
if ( i > = 10 ) break ;
}
BackButton . Enabled = ( BackSteps . Count > 0 ) ;
ForwardButton . Enabled = ( ForwardSteps . Count > 0 ) ;
}
private void UpdateSelectionUI ( )
{
var ic = MainListView . VirtualListSize ;
var sc = MainListView . SelectedIndices . Count ;
var str = ic . ToString ( ) + " item" + ( ( ic ! = 1 ) ? "s" : "" ) + " shown" ;
bool isitem = false ;
bool isfile = false ;
bool canview = false ;
bool canedit = false ;
bool canexportxml = false ;
bool canimport = false ;
if ( sc ! = 0 )
{
long bc = 0 ;
if ( CurrentFiles ! = null )
{
foreach ( int idx in MainListView . SelectedIndices )
{
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) continue ;
var file = CurrentFiles [ idx ] ;
if ( ( file . Folder = = null ) | | ( file . Folder . RpfFile ! = null ) )
{
bc + = file . FileSize ;
}
isitem = true ;
isfile = isfile | | ( file . Folder = = null ) ;
canview = canview | | CanViewFile ( file ) ;
2017-11-25 13:01:56 +08:00
canexportxml = canexportxml | | CanExportXml ( file ) ;
2017-09-21 18:33:05 +08:00
}
}
str + = ", " + sc . ToString ( ) + " selected" ;
if ( bc > 0 )
{
str + = ", " + TextUtil . GetBytesReadable ( bc ) ;
}
}
UpdateStatus ( str ) ;
EditViewMenu . Enabled = canview ;
EditViewHexMenu . Enabled = isfile ;
EditExportXmlMenu . Enabled = canexportxml ;
EditExtractRawMenu . Enabled = isfile ;
EditImportRawMenu . Visible = canimport ;
EditImportXmlMenu . Visible = canimport ;
EditImportMenuSeparator . Visible = canimport ;
EditCopyMenu . Enabled = isfile ;
EditCopyPathMenu . Enabled = isitem ;
EditRenameMenu . Visible = canedit ;
EditReplaceMenu . Visible = canedit ;
EditDeleteMenu . Visible = canedit ;
EditEditModeMenuSeparator . Visible = canedit ;
}
private TreeNode FindTreeNode ( MainTreeFolder f , TreeNode parent )
{
var tnc = ( parent ! = null ) ? parent . Nodes : MainTreeView . Nodes ;
foreach ( TreeNode node in tnc )
{
if ( node . Tag = = f )
{
return node ;
}
}
return null ;
}
private TreeNode FindTreeNode ( string text , TreeNode parent )
{
var tnc = ( parent ! = null ) ? parent . Nodes : MainTreeView . Nodes ;
foreach ( TreeNode node in tnc )
{
if ( node . Text . ToLowerInvariant ( ) = = text )
{
return node ;
}
}
return null ;
}
private void RefreshMainTreeView ( )
{
Ready = false ;
var allRpfs = new List < RpfFile > ( ) ;
ClearMainTreeView ( ) ;
2018-02-18 17:20:39 +08:00
string fullPath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2017-09-21 18:33:05 +08:00
2018-02-18 17:20:39 +08:00
string [ ] allfiles = Directory . GetFiles ( GTAFolder . CurrentGTAFolder , "*" , SearchOption . AllDirectories ) ;
2017-09-21 18:33:05 +08:00
Dictionary < string , MainTreeFolder > nodes = new Dictionary < string , MainTreeFolder > ( ) ;
MainTreeFolder root = new MainTreeFolder ( ) ;
2018-02-18 17:20:39 +08:00
root . FullPath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2017-09-21 18:33:05 +08:00
root . Path = "" ;
root . Name = "GTA V" ;
RootFolder = root ;
UpdateStatus ( "Scanning..." ) ;
foreach ( var filepath in allfiles )
{
2018-02-18 17:20:39 +08:00
var relpath = filepath . Replace ( fullPath , "" ) ;
2017-09-21 18:33:05 +08:00
var filepathl = filepath . ToLowerInvariant ( ) ;
UpdateStatus ( "Scanning " + relpath + "..." ) ;
MainTreeFolder parentnode = null , prevnode = null , node = null ;
var prevnodepath = "" ;
var idx = relpath . LastIndexOf ( '\\' ) ;
while ( idx > 0 ) //create the folder tree nodes and build up the hierarchy
{
var parentpath = relpath . Substring ( 0 , idx ) ;
var parentidx = parentpath . LastIndexOf ( '\\' ) ;
var parentname = parentpath . Substring ( parentidx + 1 ) ;
var exists = nodes . TryGetValue ( parentpath , out node ) ;
if ( ! exists )
{
2018-02-18 17:20:39 +08:00
node = CreateRootDirTreeFolder ( parentname , parentpath , fullPath + parentpath ) ;
2017-09-21 18:33:05 +08:00
nodes [ parentpath ] = node ;
}
if ( parentnode = = null )
{
parentnode = node ;
}
if ( prevnode ! = null )
{
node . AddChild ( prevnode ) ;
}
prevnode = node ;
prevnodepath = parentpath ;
idx = relpath . LastIndexOf ( '\\' , idx - 1 ) ;
if ( exists ) break ;
if ( idx < 0 )
{
root . AddChild ( node ) ;
}
}
if ( filepathl . EndsWith ( ".rpf" ) ) //add RPF nodes
{
RpfFile rpf = new RpfFile ( filepath , relpath ) ;
rpf . ScanStructure ( UpdateStatus , UpdateErrorLog ) ;
2018-01-10 11:17:30 +08:00
node = CreateRpfTreeFolder ( rpf , relpath , filepath ) ;
2017-09-21 18:33:05 +08:00
RecurseMainTreeViewRPF ( node , allRpfs ) ;
if ( parentnode ! = null )
{
parentnode . AddChild ( node ) ;
}
else
{
root . AddChild ( node ) ;
}
}
else
{
if ( parentnode ! = null )
{
parentnode . AddFile ( filepath ) ;
}
else
{
root . AddFile ( filepath ) ;
}
}
}
AddMainTreeViewRoot ( root ) ;
if ( root . Children ! = null )
{
root . Children . Sort ( ( n1 , n2 ) = > n1 . Name . CompareTo ( n2 . Name ) ) ;
foreach ( var node in root . Children )
{
AddMainTreeViewNode ( node ) ;
}
}
AllRpfs = allRpfs ;
Ready = true ;
MainTreeViewRefreshComplete ( ) ;
}
private void RecurseMainTreeViewRPF ( MainTreeFolder f , List < RpfFile > allRpfs )
{
2018-02-18 17:20:39 +08:00
var rootpath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2017-09-21 18:33:05 +08:00
var fld = f . RpfFolder ;
if ( fld ! = null )
{
if ( fld . Directories ! = null )
{
foreach ( var dir in fld . Directories )
{
2018-01-10 11:17:30 +08:00
var dtnf = CreateRpfDirTreeFolder ( dir , dir . Path , rootpath + dir . Path ) ;
2017-09-21 18:33:05 +08:00
f . AddChild ( dtnf ) ;
RecurseMainTreeViewRPF ( dtnf , allRpfs ) ;
}
}
}
var rpf = f . RpfFile ;
if ( rpf ! = null )
{
allRpfs . Add ( rpf ) ;
if ( rpf . Children ! = null )
{
foreach ( var child in rpf . Children )
{
2018-01-10 11:17:30 +08:00
var ctnf = CreateRpfTreeFolder ( child , child . Path , rootpath + child . Path ) ;
2017-09-21 18:33:05 +08:00
f . AddChildToHierarchy ( ctnf ) ;
RecurseMainTreeViewRPF ( ctnf , allRpfs ) ;
}
}
2018-01-10 11:17:30 +08:00
//JenkIndex.Ensure(rpf.Name);
2017-09-21 18:33:05 +08:00
if ( rpf . AllEntries ! = null )
{
foreach ( RpfEntry entry in rpf . AllEntries )
{
if ( string . IsNullOrEmpty ( entry . NameLower ) ) continue ;
2018-01-10 11:17:30 +08:00
var shortnamel = entry . GetShortNameLower ( ) ;
JenkIndex . Ensure ( shortnamel ) ;
entry . ShortNameHash = JenkHash . GenHash ( shortnamel ) ;
2017-09-21 18:33:05 +08:00
entry . NameHash = JenkHash . GenHash ( entry . NameLower ) ;
}
}
}
}
private void ClearMainTreeView ( )
{
try
{
if ( InvokeRequired )
{
Invoke ( new Action ( ( ) = > { ClearMainTreeView ( ) ; } ) ) ;
}
else
{
GoButton . Enabled = false ;
UpButton . Enabled = false ;
BackButton . Enabled = false ;
ForwardButton . Enabled = false ;
RefreshButton . Enabled = false ;
SearchButton . Enabled = false ;
2018-01-10 11:17:30 +08:00
EditModeButton . Enabled = false ;
2017-09-21 18:33:05 +08:00
MainTreeView . Nodes . Clear ( ) ;
MainListView . VirtualListSize = 0 ; //also clear the list view...
}
}
catch { }
}
private void AddMainTreeViewRoot ( MainTreeFolder f )
{
try
{
if ( InvokeRequired )
{
Invoke ( new Action ( ( ) = > { AddMainTreeViewRoot ( f ) ; } ) ) ;
}
else
{
var rn = MainTreeView . Nodes . Add ( f . Path , f . Name , 0 , 0 ) ; //ROOT imageIndex
rn . ToolTipText = f . FullPath ;
rn . Tag = f ;
}
}
catch { }
}
private void AddMainTreeViewNode ( MainTreeFolder f )
{
try
{
if ( InvokeRequired )
{
Invoke ( new Action ( ( ) = > { AddMainTreeViewNode ( f ) ; } ) ) ;
}
else
{
string n = f . Name ;
var root = ( MainTreeView . Nodes . Count > 0 ) ? MainTreeView . Nodes [ 0 ] : null ;
RecurseAddMainTreeViewNodes ( f , root ) ;
root . Expand ( ) ;
}
}
catch { }
}
private void RecurseAddMainTreeViewNodes ( MainTreeFolder f , TreeNode parent )
{
int imgIndex = 1 ; //FOLDER imageIndex
if ( f . RpfFile ! = null ) imgIndex = 3 ; //RPF FILE imageIndex
var nc = ( parent ! = null ) ? parent . Nodes : MainTreeView . Nodes ;
var tn = nc . Add ( f . Path , f . Name , imgIndex , imgIndex ) ;
tn . ToolTipText = f . Path ;
tn . Tag = f ;
2018-01-10 11:17:30 +08:00
f . TreeNode = tn ;
2017-09-21 18:33:05 +08:00
if ( f . Children ! = null )
{
f . Children . Sort ( ( n1 , n2 ) = > n1 . Name . CompareTo ( n2 . Name ) ) ;
foreach ( var child in f . Children )
{
RecurseAddMainTreeViewNodes ( child , tn ) ;
}
}
}
private void MainTreeViewRefreshComplete ( )
{
try
{
if ( InvokeRequired )
{
Invoke ( new Action ( ( ) = > { MainTreeViewRefreshComplete ( ) ; } ) ) ;
}
else
{
if ( CurrentFolder ! = null )
{
if ( CurrentFolder . IsSearchResults )
{
Search ( CurrentFolder . SearchTerm ) ;
}
else
{
var path = CurrentFolder . Path ;
CurrentFolder = null ;
Navigate ( path ) ;
}
}
else
{
Navigate ( RootFolder ) ;
}
}
}
catch { }
}
2018-01-10 11:17:30 +08:00
private void AddNewFolderTreeNode ( MainTreeFolder f )
{
if ( CurrentFolder = = null ) return ;
RecurseAddMainTreeViewNodes ( f , CurrentFolder . TreeNode ) ;
CurrentFolder . AddChild ( f ) ;
CurrentFolder . ListItems = null ;
RefreshMainListView ( ) ;
}
private MainTreeFolder CreateRpfTreeFolder ( RpfFile rpf , string relpath , string fullpath )
{
var node = new MainTreeFolder ( ) ;
node . RpfFile = rpf ;
node . RpfFolder = rpf . Root ;
node . Name = rpf . Name ;
node . Path = relpath ;
node . FullPath = fullpath ;
return node ;
}
private MainTreeFolder CreateRpfDirTreeFolder ( RpfDirectoryEntry dir , string relpath , string fullpath )
{
var node = new MainTreeFolder ( ) ;
node . RpfFolder = dir ;
node . Name = dir . Name ;
node . Path = relpath ;
node . FullPath = fullpath ;
return node ;
}
private MainTreeFolder CreateRootDirTreeFolder ( string name , string path , string fullpath )
{
var node = new MainTreeFolder ( ) ;
node . Name = name ;
node . Path = path ;
node . FullPath = fullpath ;
return node ;
}
private void RenameTreeFolder ( MainTreeFolder f , string newname )
{
if ( f . Parent = = null ) return ;
f . Name = newname ;
f . Path = f . Parent . Path + "\\" + newname . ToLowerInvariant ( ) ;
f . FullPath = f . Parent . FullPath + "\\" + newname ;
if ( f . TreeNode ! = null )
{
f . TreeNode . Text = newname ;
}
if ( f . Children ! = null )
{
foreach ( var item in f . Children )
{
RenameTreeFolder ( item , item . Name ) ; //just to make sure the all the paths are correct...
}
}
if ( f . ListItems ! = null )
{
foreach ( var item in f . ListItems )
{
RenameListItem ( item , item . Name ) ;
}
}
}
private void RenameListItem ( MainListItem i , string newname )
{
if ( i . Parent = = null ) return ;
i . Name = newname ;
i . Path = i . Parent . Path + "\\" + newname . ToLowerInvariant ( ) ;
i . FullPath = i . Parent . FullPath + "\\" + newname ;
if ( i . Parent = = CurrentFolder )
{
int idx = CurrentFiles . IndexOf ( i ) ;
if ( idx > = 0 )
{
MainListView . RedrawItems ( idx , idx , false ) ;
}
}
}
private void RemoveTreeFolder ( MainTreeFolder f )
{
if ( f . Parent = = null ) return ;
f . Parent . Children ? . Remove ( f ) ;
if ( f . TreeNode ! = null )
{
f . TreeNode . Remove ( ) ;
}
}
private void RemoveListItem ( MainListItem i )
{
if ( i . Parent = = null ) return ;
MainListView . VirtualListSize = 0 ;
i . Parent . ListItems ? . Remove ( i ) ;
if ( i . Parent = = CurrentFolder )
{
CurrentFiles . Remove ( i ) ; //should really be the same list as above, but just in case...
}
MainListView . VirtualListSize = CurrentFiles . Count ;
}
2017-09-21 18:33:05 +08:00
private void RefreshMainListView ( )
{
MainListView . VirtualListSize = 0 ;
if ( CurrentFolder ! = null )
{
CurrentFiles = CurrentFolder . GetListItems ( ) ;
foreach ( var file in CurrentFiles ) //cache all the data for use by the list view.
{
file . CacheDetails ( this ) ;
}
SortMainListView ( SortColumnIndex , SortDirection ) ; //sorts CurrentItems and sets VirtualListSize
ShowMainListViewPathColumn ( CurrentFolder . IsSearchResults ) ;
}
else
{
UpdateSelectionUI ( ) ;
ShowMainListViewPathColumn ( false ) ;
}
}
private void SortMainListView ( int col , SortOrder dir )
{
if ( dir = = SortOrder . None ) dir = SortOrder . Ascending ; //none not supported for actual sorting!
SortColumnIndex = col ;
SortDirection = dir ;
MainListView . SetSortIcon ( col , dir ) ;
MainListView . VirtualListSize = 0 ;
if ( CurrentFiles = = null ) return ;
CurrentFiles . Sort ( ( i1 , i2 ) = > i1 . SortCompare ( i2 , col , dir ) ) ;
MainListView . VirtualListSize = CurrentFiles . Count ;
UpdateSelectionUI ( ) ;
}
private void ShowMainListViewPathColumn ( bool show )
{
bool visible = ( MainPathColumnHeader . Width > 0 ) ;
if ( show & & ! visible )
{
MainPathColumnHeader . Width = PreviousPathColumnWidth ;
}
else if ( ! show & & visible )
{
PreviousPathColumnWidth = MainPathColumnHeader . Width ;
MainPathColumnHeader . Width = 0 ;
if ( SortColumnIndex = = 4 ) //path col
{
SortMainListView ( 0 , SortDirection ) ; //switch sort to name col
}
}
}
public void Search ( string text )
{
if ( ! Ready ) return ;
if ( Searching ) return ;
if ( string . IsNullOrEmpty ( text ) ) return ;
SearchTextBox . Text = text ;
SearchButton . Image = SearchGlobalButton . Image ;
SearchButton . Text = SearchGlobalButton . Text ;
SearchGlobalButton . Checked = true ;
SearchFilterButton . Checked = false ;
SearchResults = new MainTreeFolder ( ) ;
SearchResults . Name = "Search Results: " + text ;
SearchResults . Path = SearchResults . Name ;
SearchResults . IsSearchResults = true ;
SearchResults . SearchTerm = text ;
Navigate ( SearchResults ) ;
SortDirection = SortOrder . None ;
MainListView . SetSortIcon ( SortColumnIndex , SortDirection ) ;
MainListView . VirtualListSize = 0 ;
CurrentFiles . Clear ( ) ;
UpdateStatus ( "Searching..." ) ;
var term = text . ToLowerInvariant ( ) ;
//Task.Run(() =>
//{
Searching = true ;
Cursor = Cursors . WaitCursor ;
var resultcount = RootFolder . Search ( term , this ) ;
if ( Searching )
{
Searching = false ;
UpdateStatus ( "Search complete. " + resultcount . ToString ( ) + " items found." ) ;
}
else
{
UpdateStatus ( "Search aborted. " + resultcount . ToString ( ) + " items found." ) ;
}
Cursor = Cursors . Default ;
//});
}
public void Filter ( string text )
{
SearchTextBox . Text = text ;
SearchButton . Image = SearchFilterButton . Image ;
SearchButton . Text = SearchFilterButton . Text ;
SearchGlobalButton . Checked = false ;
SearchFilterButton . Checked = true ;
//TODO!
MessageBox . Show ( "Filter TODO!" ) ;
}
public void AddSearchResult ( MainListItem item )
{
if ( SearchResults = = null ) return ;
if ( SearchResults . ListItems ! = CurrentFiles ) return ;
if ( item ! = null )
{
item . CacheDetails ( this ) ;
CurrentFiles . Add ( item ) ;
}
else
{
MainListView . VirtualListSize = CurrentFiles . Count ;
}
//try
//{
// if (InvokeRequired)
// {
// BeginInvoke(new Action(() => { AddSearchResultInner(item); }));
// }
// else
// {
// AddSearchResultInner(item);
// }
//}
//catch { }
}
private byte [ ] GetFileData ( MainListItem file )
{
byte [ ] data = null ;
2018-01-11 09:10:03 +08:00
if ( file . Folder ! = null )
{
var entry = file . Folder . RpfFile ? . ParentFileEntry ;
if ( entry ! = null )
{
data = entry . File . ExtractFile ( entry ) ; //extract an RPF from another.
}
}
else if ( file . File ! = null )
2017-09-21 18:33:05 +08:00
{
//load file from RPF
2018-01-11 09:10:03 +08:00
if ( file . File . File ! = null ) //need the reference to the RPF archive
{
data = file . File . File . ExtractFile ( file . File ) ;
}
2017-09-21 18:33:05 +08:00
}
else if ( ! string . IsNullOrEmpty ( file . FullPath ) )
{
//load file from filesystem
data = File . ReadAllBytes ( file . FullPath ) ;
}
return data ;
}
private bool CanViewFile ( MainListItem item )
{
if ( item = = null ) return false ;
if ( item . FileType = = null ) return false ;
switch ( item . FileType . DefaultAction )
{
case FileTypeAction . ViewText :
case FileTypeAction . ViewXml :
case FileTypeAction . ViewYtd :
case FileTypeAction . ViewYmt :
case FileTypeAction . ViewYmf :
case FileTypeAction . ViewYmap :
case FileTypeAction . ViewYtyp :
case FileTypeAction . ViewJPso :
case FileTypeAction . ViewModel :
case FileTypeAction . ViewCut :
case FileTypeAction . ViewAwc :
case FileTypeAction . ViewGxt :
case FileTypeAction . ViewRel :
case FileTypeAction . ViewFxc :
case FileTypeAction . ViewYwr :
case FileTypeAction . ViewYvr :
2017-09-29 20:23:37 +08:00
case FileTypeAction . ViewYcd :
2017-12-14 18:49:41 +08:00
case FileTypeAction . ViewCacheDat :
2017-09-21 18:33:05 +08:00
return true ;
case FileTypeAction . ViewHex :
default :
break ;
}
return false ;
}
2017-10-04 15:44:29 +08:00
private bool CanExportXml ( MainListItem item )
{
if ( item = = null ) return false ;
if ( item . FileType = = null ) return false ;
switch ( item . FileType . DefaultAction )
{
case FileTypeAction . ViewYmt :
case FileTypeAction . ViewYmf :
case FileTypeAction . ViewYmap :
case FileTypeAction . ViewYtyp :
case FileTypeAction . ViewJPso :
2018-01-11 11:06:48 +08:00
case FileTypeAction . ViewCut :
2017-10-04 15:44:29 +08:00
return true ;
}
return false ;
}
2017-09-21 18:33:05 +08:00
private void View ( MainListItem item )
{
#if ! DEBUG
try
#endif
{
byte [ ] data = null ;
string name = "" ;
string path = "" ;
if ( item . File ! = null )
{
//load file from RPF
if ( item . File . File = = null ) return ; //no RPF file? go no further
data = item . File . File . ExtractFile ( item . File ) ;
name = item . Name ;
path = item . FullPath ;
}
else if ( ! string . IsNullOrEmpty ( item . FullPath ) )
{
//load file from filesystem
data = File . ReadAllBytes ( item . FullPath ) ;
name = new FileInfo ( item . FullPath ) . Name ;
path = item . FullPath ;
}
if ( data = = null ) return ;
var ft = item . FileType ;
switch ( ft . DefaultAction )
{
case FileTypeAction . ViewText :
ViewText ( name , path , data ) ;
break ;
case FileTypeAction . ViewXml :
ViewXml ( name , path , data ) ;
break ;
case FileTypeAction . ViewYtd :
ViewYtd ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewYmt :
ViewYmt ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewYmf :
ViewYmf ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewYmap :
ViewYmap ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewYtyp :
ViewYtyp ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewJPso :
ViewJPso ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewCut :
ViewCut ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewModel :
ViewModel ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewAwc :
ViewAwc ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewGxt :
ViewGxt ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewRel :
ViewRel ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewFxc :
ViewFxc ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewYwr :
ViewYwr ( name , path , data , item . File ) ;
break ;
case FileTypeAction . ViewYvr :
ViewYvr ( name , path , data , item . File ) ;
break ;
2017-09-29 20:23:37 +08:00
case FileTypeAction . ViewYcd :
ViewYcd ( name , path , data , item . File ) ;
break ;
2017-12-14 18:49:41 +08:00
case FileTypeAction . ViewCacheDat :
ViewCacheDat ( name , path , data , item . File ) ;
break ;
2017-09-21 18:33:05 +08:00
case FileTypeAction . ViewHex :
default :
ViewHex ( name , path , data ) ;
break ;
}
}
#if ! DEBUG
catch ( Exception ex )
{
UpdateErrorLog ( ex . ToString ( ) ) ;
return ;
}
#endif
}
private void ViewHex ( MainListItem item )
{
try
{
byte [ ] data = null ;
string name = "" ;
string path = "" ;
if ( item . File ! = null )
{
//load file from RPF
if ( item . File . File = = null ) return ; //no RPF file? go no further
data = item . File . File . ExtractFile ( item . File ) ;
name = item . Name ;
path = item . FullPath ;
}
else if ( ! string . IsNullOrEmpty ( item . FullPath ) )
{
//load file from filesystem
data = File . ReadAllBytes ( item . FullPath ) ;
name = new FileInfo ( item . FullPath ) . Name ;
path = item . FullPath ;
}
if ( data = = null ) return ;
ViewHex ( name , path , data ) ;
}
catch ( Exception ex )
{
UpdateErrorLog ( ex . ToString ( ) ) ;
return ;
}
}
private void ViewHex ( string name , string path , byte [ ] data )
{
HexForm f = new HexForm ( ) ;
f . Show ( ) ;
f . LoadData ( name , path , data ) ;
}
private void ViewXml ( string name , string path , byte [ ] data )
{
string xml = Encoding . UTF8 . GetString ( data ) ;
XmlForm f = new XmlForm ( ) ;
f . Show ( ) ;
f . LoadXml ( name , path , xml ) ;
}
private void ViewText ( string name , string path , byte [ ] data )
{
string txt = Encoding . UTF8 . GetString ( data ) ;
TextForm f = new TextForm ( ) ;
f . Show ( ) ;
f . LoadText ( name , path , txt ) ;
}
private void ViewYtd ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ytd = RpfFile . GetFile < YtdFile > ( e , data ) ;
YtdForm f = new YtdForm ( ) ;
f . Show ( ) ;
f . LoadYtd ( ytd ) ;
}
private void ViewYmt ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ymt = RpfFile . GetFile < YmtFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( ymt ) ;
}
private void ViewYmf ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ymf = RpfFile . GetFile < YmfFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( ymf ) ;
}
private void ViewYmap ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ymap = RpfFile . GetFile < YmapFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( ymap ) ;
}
private void ViewYtyp ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ytyp = RpfFile . GetFile < YtypFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( ytyp ) ;
}
private void ViewJPso ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var pso = RpfFile . GetFile < JPsoFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( pso ) ;
}
private void ViewModel ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var nl = e ? . NameLower ? ? "" ;
var fe = new FileInfo ( nl ) . Extension ;
ModelForm f = new ModelForm ( this ) ;
f . Show ( ) ;
switch ( fe )
{
case ".ydr" :
var ydr = RpfFile . GetFile < YdrFile > ( e , data ) ;
f . LoadModel ( ydr ) ;
break ;
case ".ydd" :
var ydd = RpfFile . GetFile < YddFile > ( e , data ) ;
f . LoadModels ( ydd ) ;
break ;
case ".yft" :
var yft = RpfFile . GetFile < YftFile > ( e , data ) ;
f . LoadModel ( yft ) ;
break ;
case ".ybn" :
var ybn = RpfFile . GetFile < YbnFile > ( e , data ) ;
f . LoadModel ( ybn ) ;
break ;
case ".ypt" :
var ypt = RpfFile . GetFile < YptFile > ( e , data ) ;
f . LoadParticles ( ypt ) ;
break ;
case ".ynv" :
var ynv = RpfFile . GetFile < YnvFile > ( e , data ) ;
f . LoadNavmesh ( ynv ) ;
break ;
}
}
private void ViewCut ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var cut = RpfFile . GetFile < CutFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( cut ) ;
}
private void ViewAwc ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var awc = RpfFile . GetFile < AwcFile > ( e , data ) ;
AwcForm f = new AwcForm ( ) ;
f . Show ( ) ;
f . LoadAwc ( awc ) ;
}
private void ViewGxt ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var gxt = RpfFile . GetFile < Gxt2File > ( e , data ) ;
GxtForm f = new GxtForm ( ) ;
f . Show ( ) ;
f . LoadGxt2 ( gxt ) ;
}
private void ViewRel ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var rel = RpfFile . GetFile < RelFile > ( e , data ) ;
RelForm f = new RelForm ( ) ;
f . Show ( ) ;
f . LoadRel ( rel ) ;
}
private void ViewFxc ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var fxc = RpfFile . GetFile < FxcFile > ( e , data ) ;
FxcForm f = new FxcForm ( ) ;
f . Show ( ) ;
f . LoadFxc ( fxc ) ;
}
private void ViewYwr ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ywr = RpfFile . GetFile < YwrFile > ( e , data ) ;
YwrForm f = new YwrForm ( ) ;
f . Show ( ) ;
f . LoadYwr ( ywr ) ;
}
private void ViewYvr ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var yvr = RpfFile . GetFile < YvrFile > ( e , data ) ;
YvrForm f = new YvrForm ( ) ;
f . Show ( ) ;
f . LoadYvr ( yvr ) ;
}
2017-09-29 20:23:37 +08:00
private void ViewYcd ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var ycd = RpfFile . GetFile < YcdFile > ( e , data ) ;
YcdForm f = new YcdForm ( ) ;
f . Show ( ) ;
f . LoadYcd ( ycd ) ;
}
2017-12-14 18:49:41 +08:00
private void ViewCacheDat ( string name , string path , byte [ ] data , RpfFileEntry e )
{
var cachedat = RpfFile . GetFile < CacheDatFile > ( e , data ) ;
MetaForm f = new MetaForm ( ) ;
f . Show ( ) ;
f . LoadMeta ( cachedat ) ;
}
2017-09-21 18:33:05 +08:00
private void ShowTreeContextMenu ( TreeNode n , Point p )
{
var f = n ? . Tag as MainTreeFolder ;
bool filesys = ( ( f . RpfFolder = = null ) & & ( f . RpfFile = = null ) ) ;
bool expanded = ( ( n ! = null ) & & ( n . IsExpanded ) ) ;
bool collapsed = ( ( n ! = null ) & & ( ! n . IsExpanded ) ) ;
if ( ( f . RpfFile ! = null ) & & ( f . RpfFile . Parent = = null ) )
{
filesys = true ; //allow viewing root RPF's in explorer
}
TreeContextWinExplorerMenu . Enabled = filesys ;
TreeContextExpandMenu . Enabled = collapsed ;
TreeContextCollapseMenu . Enabled = expanded ;
TreeContextMenu . Show ( MainTreeView , p ) ;
}
private void ShowListContextMenu ( MainListItem item )
{
bool isitem = false ;
bool isfile = false ;
bool isfolder = false ;
2018-01-11 09:10:03 +08:00
bool isarchive = false ;
2017-09-21 18:33:05 +08:00
bool isfilesys = false ;
2018-01-10 11:17:30 +08:00
bool issearch = CurrentFolder ? . IsSearchResults ? ? false ;
2017-09-21 18:33:05 +08:00
bool canview = false ;
bool canexportxml = false ;
2018-01-11 09:10:03 +08:00
bool canextract = false ;
2018-01-10 11:17:30 +08:00
bool canimport = EditMode & & ( CurrentFolder ? . RpfFolder ! = null ) & & ! issearch ;
2018-01-11 11:06:48 +08:00
bool cancreate = EditMode & & ! issearch ;
2017-09-21 18:33:05 +08:00
bool canedit = false ;
2018-01-12 12:19:32 +08:00
bool candefrag = false ;
2017-09-21 18:33:05 +08:00
if ( item ! = null )
{
2018-01-11 09:10:03 +08:00
var entry = item . GetRpfEntry ( ) ;
isitem = true ;
isfilesys = ( entry = = null ) ;
isarchive = ( item . Folder ? . RpfFile ! = null ) ;
2017-09-21 18:33:05 +08:00
isfolder = ( item . Folder ! = null ) ;
2018-01-11 09:10:03 +08:00
isfile = ! isfolder ;
2017-09-21 18:33:05 +08:00
canview = CanViewFile ( item ) ;
2017-10-04 15:44:29 +08:00
canexportxml = CanExportXml ( item ) ;
2018-01-10 11:17:30 +08:00
canedit = EditMode & & ! issearch ;
2018-01-11 09:10:03 +08:00
canextract = isfile | | ( isarchive & & ! isfilesys ) ;
2018-01-12 12:19:32 +08:00
candefrag = isarchive & & canedit ;
2017-09-21 18:33:05 +08:00
}
ListContextViewMenu . Enabled = canview ;
ListContextViewHexMenu . Enabled = isfile ;
ListContextExportXmlMenu . Enabled = canexportxml ;
2018-01-11 09:10:03 +08:00
ListContextExtractRawMenu . Enabled = canextract ;
2017-09-27 00:59:39 +08:00
ListContextExtractUncompressedMenu . Enabled = isfile ;
2017-09-21 18:33:05 +08:00
2018-01-11 11:06:48 +08:00
ListContextNewMenu . Visible = cancreate ;
2017-09-21 18:33:05 +08:00
ListContextImportRawMenu . Visible = canimport ;
ListContextImportXmlMenu . Visible = canimport ;
2018-01-11 11:06:48 +08:00
ListContextImportSeparator . Visible = cancreate ;
2017-09-21 18:33:05 +08:00
ListContextCopyMenu . Enabled = isfile ;
ListContextCopyPathMenu . Enabled = isitem ;
ListContextOpenFileLocationMenu . Visible = issearch ;
ListContextOpenFileLocationSeparator . Visible = issearch ;
ListContextRenameMenu . Visible = canedit ;
ListContextReplaceMenu . Visible = canedit ;
ListContextDeleteMenu . Visible = canedit ;
ListContextEditSeparator . Visible = canedit ;
2018-01-12 12:19:32 +08:00
ListContextDefragmentMenu . Visible = candefrag ;
ListContextDefragmentSeparator . Visible = candefrag ;
2017-09-21 18:33:05 +08:00
ListContextMenu . Show ( Cursor . Position ) ;
}
2018-01-10 11:17:30 +08:00
private void EnableEditMode ( bool enable )
{
if ( EditMode = = enable )
{
return ;
}
if ( enable )
{
if ( MessageBox . Show ( this , "While in edit mode, all changes are automatically saved.\nDo you want to continue?" , "Warning - Entering edit mode" , MessageBoxButtons . YesNo , MessageBoxIcon . Warning ) ! = DialogResult . Yes )
{
return ;
}
}
EditMode = enable ;
EditModeButton . Checked = enable ;
MainListView . LabelEdit = enable ;
2018-01-11 12:03:58 +08:00
EnsureEditModeWarning ( ) ;
2018-01-10 11:17:30 +08:00
}
2018-01-11 12:03:58 +08:00
private void EnsureEditModeWarning ( )
{
2018-01-12 12:19:32 +08:00
bool mods = CurrentFolder . Path . ToLowerInvariant ( ) . StartsWith ( "mods" ) ;
bool srch = CurrentFolder ? . IsSearchResults ? ? false ;
bool show = EditMode & & ! mods & & ! srch ;
2018-01-11 12:03:58 +08:00
int gap = 3 ;
int bot = MainListView . Bottom ;
EditModeBaseWarningPanel . Top = gap ;
EditModeModsWarningPanel . Top = gap ;
EditModeModsWarningPanel . Visible = false ;
2018-01-10 11:17:30 +08:00
2018-01-11 12:03:58 +08:00
MainListView . Top = show ? EditModeBaseWarningPanel . Bottom + gap : gap ;
MainListView . Height = bot - MainListView . Top ;
}
2018-01-10 11:17:30 +08:00
private bool IsFilenameOk ( string name )
{
foreach ( var ic in Path . GetInvalidFileNameChars ( ) )
{
if ( name . Contains ( ic ) )
{
return false ;
}
}
return true ;
}
2018-01-11 09:10:03 +08:00
private bool EnsureRpfValidEncryption ( )
{
if ( CurrentFolder . RpfFolder = = null ) return false ;
var rpf = CurrentFolder . RpfFolder . File ;
2018-01-12 12:19:32 +08:00
return EnsureRpfValidEncryption ( rpf ) ;
}
private bool EnsureRpfValidEncryption ( RpfFile rpf )
{
2018-01-11 09:10:03 +08:00
if ( rpf = = null ) return false ;
bool needsupd = false ;
var f = rpf ;
List < RpfFile > files = new List < RpfFile > ( ) ;
while ( f ! = null )
{
if ( f . Encryption ! = RpfEncryption . OPEN )
{
var msg = "Archive " + f . Name + " is currently set to " + f . Encryption . ToString ( ) + " encryption.\nAre you sure you want to change this archive to OPEN encryption?\nLoading by the game will require OpenIV.asi." ;
if ( MessageBox . Show ( msg , "Change RPF encryption type" , MessageBoxButtons . YesNo ) ! = DialogResult . Yes )
{
return false ;
}
needsupd = true ;
}
if ( needsupd )
{
files . Add ( f ) ;
}
f = f . Parent ;
}
//change encryption types, starting from the root rpf.
files . Reverse ( ) ;
foreach ( var file in files )
{
RpfFile . SetEncryptionType ( file , RpfEncryption . OPEN ) ;
}
return true ;
}
2017-09-21 18:33:05 +08:00
private void ViewSelected ( )
{
for ( int i = 0 ; i < MainListView . SelectedIndices . Count ; i + + )
{
var idx = MainListView . SelectedIndices [ i ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) continue ;
var file = CurrentFiles [ idx ] ;
if ( file . Folder = = null )
{
View ( file ) ;
}
}
}
private void ViewSelectedHex ( )
{
for ( int i = 0 ; i < MainListView . SelectedIndices . Count ; i + + )
{
var idx = MainListView . SelectedIndices [ i ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) continue ;
var file = CurrentFiles [ idx ] ;
if ( file . Folder = = null )
{
ViewHex ( file ) ;
}
}
}
private void ExportXml ( )
{
2017-10-04 15:44:29 +08:00
if ( MainListView . SelectedIndices . Count = = 1 )
2017-09-21 18:33:05 +08:00
{
2017-10-04 15:44:29 +08:00
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) return ;
2017-09-21 18:33:05 +08:00
var file = CurrentFiles [ idx ] ;
if ( file . Folder = = null )
{
2017-10-04 15:44:29 +08:00
if ( CanExportXml ( file ) )
{
byte [ ] data = GetFileData ( file ) ;
if ( data = = null )
{
MessageBox . Show ( "Unable to extract file: " + file . Path ) ;
return ;
}
string newfn ;
string xml = MetaXml . GetXml ( file . File , data , out newfn ) ;
if ( string . IsNullOrEmpty ( xml ) )
{
MessageBox . Show ( "Unable to convert file to XML: " + file . Path ) ;
return ;
}
SaveFileDialog . FileName = newfn ;
if ( SaveFileDialog . ShowDialog ( ) = = DialogResult . OK )
{
string path = SaveFileDialog . FileName ;
try
{
File . WriteAllText ( path , xml ) ;
}
catch ( Exception ex )
{
MessageBox . Show ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
}
}
else
{
if ( FolderBrowserDialog . ShowDialog ( ) ! = DialogResult . OK ) return ;
string folderpath = FolderBrowserDialog . SelectedPath ;
if ( ! folderpath . EndsWith ( "\\" ) ) folderpath + = "\\" ;
StringBuilder errors = new StringBuilder ( ) ;
for ( int i = 0 ; i < MainListView . SelectedIndices . Count ; i + + )
{
var idx = MainListView . SelectedIndices [ i ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) continue ;
var file = CurrentFiles [ idx ] ;
if ( file . Folder = = null )
{
if ( ! CanExportXml ( file ) ) continue ;
var data = GetFileData ( file ) ;
if ( data = = null )
{
errors . AppendLine ( "Unable to extract file: " + file . Path ) ;
continue ;
}
string newfn ;
string xml = MetaXml . GetXml ( file . File , data , out newfn ) ;
if ( string . IsNullOrEmpty ( xml ) )
{
errors . AppendLine ( "Unable to convert file to XML: " + file . Path ) ;
continue ;
}
var path = folderpath + newfn ;
try
{
File . WriteAllText ( path , xml ) ;
}
catch ( Exception ex )
{
errors . AppendLine ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
string errstr = errors . ToString ( ) ;
if ( ! string . IsNullOrEmpty ( errstr ) )
{
MessageBox . Show ( "Errors were encountered:\n" + errstr ) ;
2017-09-21 18:33:05 +08:00
}
}
}
private void ExtractRaw ( )
{
if ( MainListView . SelectedIndices . Count = = 1 )
{
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) return ;
var file = CurrentFiles [ idx ] ;
2018-01-11 09:10:03 +08:00
if ( ( file . Folder = = null ) | | ( file . Folder . RpfFile ! = null ) )
2017-09-21 18:33:05 +08:00
{
byte [ ] data = GetFileData ( file ) ;
if ( data = = null )
{
MessageBox . Show ( "Unable to extract file: " + file . Path ) ;
return ;
}
2017-09-27 00:59:39 +08:00
RpfResourceFileEntry rrfe = file . File as RpfResourceFileEntry ;
if ( rrfe ! = null ) //add resource header if this is a resource file.
{
2018-01-11 09:10:03 +08:00
data = ResourceBuilder . Compress ( data ) ; //not completely ideal to recompress it...
2017-09-27 00:59:39 +08:00
data = ResourceBuilder . AddResourceHeader ( rrfe , data ) ;
}
SaveFileDialog . FileName = file . Name ;
if ( SaveFileDialog . ShowDialog ( ) = = DialogResult . OK )
{
string path = SaveFileDialog . FileName ;
try
{
File . WriteAllBytes ( path , data ) ;
}
catch ( Exception ex )
{
MessageBox . Show ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
}
else
{
if ( FolderBrowserDialog . ShowDialog ( ) ! = DialogResult . OK ) return ;
string folderpath = FolderBrowserDialog . SelectedPath ;
if ( ! folderpath . EndsWith ( "\\" ) ) folderpath + = "\\" ;
StringBuilder errors = new StringBuilder ( ) ;
for ( int i = 0 ; i < MainListView . SelectedIndices . Count ; i + + )
{
var idx = MainListView . SelectedIndices [ i ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) continue ;
var file = CurrentFiles [ idx ] ;
2018-01-11 09:10:03 +08:00
if ( ( file . Folder = = null ) | | ( file . Folder . RpfFile ! = null ) )
2017-09-27 00:59:39 +08:00
{
var path = folderpath + file . Name ;
var data = GetFileData ( file ) ;
if ( data = = null )
{
errors . AppendLine ( "Unable to extract file: " + file . Path ) ;
continue ;
}
try
{
RpfResourceFileEntry rrfe = file . File as RpfResourceFileEntry ;
if ( rrfe ! = null ) //add resource header if this is a resource file.
{
2018-01-11 09:10:03 +08:00
data = ResourceBuilder . Compress ( data ) ; //not completely ideal to recompress it...
2017-09-27 00:59:39 +08:00
data = ResourceBuilder . AddResourceHeader ( rrfe , data ) ;
}
File . WriteAllBytes ( path , data ) ;
}
catch ( Exception ex )
{
errors . AppendLine ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
string errstr = errors . ToString ( ) ;
if ( ! string . IsNullOrEmpty ( errstr ) )
{
MessageBox . Show ( "Errors were encountered:\n" + errstr ) ;
}
}
}
private void ExtractUncompressed ( )
{
if ( MainListView . SelectedIndices . Count = = 1 )
{
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) return ;
var file = CurrentFiles [ idx ] ;
if ( file . Folder = = null )
{
byte [ ] data = GetFileData ( file ) ;
if ( data = = null )
{
MessageBox . Show ( "Unable to extract file: " + file . Path ) ;
return ;
}
2017-09-21 18:33:05 +08:00
SaveFileDialog . FileName = file . Name ;
if ( SaveFileDialog . ShowDialog ( ) = = DialogResult . OK )
{
string path = SaveFileDialog . FileName ;
try
{
File . WriteAllBytes ( path , data ) ;
}
catch ( Exception ex )
{
MessageBox . Show ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
}
else
{
if ( FolderBrowserDialog . ShowDialog ( ) ! = DialogResult . OK ) return ;
string folderpath = FolderBrowserDialog . SelectedPath ;
if ( ! folderpath . EndsWith ( "\\" ) ) folderpath + = "\\" ;
StringBuilder errors = new StringBuilder ( ) ;
for ( int i = 0 ; i < MainListView . SelectedIndices . Count ; i + + )
{
var idx = MainListView . SelectedIndices [ i ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) continue ;
var file = CurrentFiles [ idx ] ;
if ( file . Folder = = null )
{
var path = folderpath + file . Name ;
var data = GetFileData ( file ) ;
if ( data = = null )
{
errors . AppendLine ( "Unable to extract file: " + file . Path ) ;
continue ;
}
try
{
File . WriteAllBytes ( path , data ) ;
}
catch ( Exception ex )
{
errors . AppendLine ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
string errstr = errors . ToString ( ) ;
if ( ! string . IsNullOrEmpty ( errstr ) )
{
MessageBox . Show ( "Errors were encountered:\n" + errstr ) ;
}
}
}
private void ExtractAll ( )
{
if ( CurrentFiles = = null ) return ;
if ( FolderBrowserDialog . ShowDialog ( ) ! = DialogResult . OK ) return ;
string folderpath = FolderBrowserDialog . SelectedPath ;
if ( ! folderpath . EndsWith ( "\\" ) ) folderpath + = "\\" ;
StringBuilder errors = new StringBuilder ( ) ;
foreach ( var file in CurrentFiles )
{
2018-01-11 09:10:03 +08:00
if ( ( file . Folder = = null ) | | ( file . Folder . RpfFile ! = null ) )
2017-09-21 18:33:05 +08:00
{
var path = folderpath + file . Name ;
var data = GetFileData ( file ) ;
if ( data = = null )
{
errors . AppendLine ( "Unable to extract file: " + file . Path ) ;
continue ;
}
try
{
2017-09-27 00:59:39 +08:00
RpfResourceFileEntry rrfe = file . File as RpfResourceFileEntry ;
if ( rrfe ! = null ) //add resource header if this is a resource file.
{
data = ResourceBuilder . Compress ( data ) ;
data = ResourceBuilder . AddResourceHeader ( rrfe , data ) ;
}
2017-09-21 18:33:05 +08:00
File . WriteAllBytes ( path , data ) ;
}
catch ( Exception ex )
{
errors . AppendLine ( "Error saving file " + path + ":\n" + ex . ToString ( ) ) ;
}
}
}
string errstr = errors . ToString ( ) ;
if ( ! string . IsNullOrEmpty ( errstr ) )
{
MessageBox . Show ( "Errors were encountered:\n" + errstr ) ;
}
}
2018-01-10 11:17:30 +08:00
private void NewFolder ( )
{
if ( CurrentFolder = = null ) return ; //shouldn't happen
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
string fname = Prompt . ShowDialog ( this , "Enter a name for the new folder:" , "Create folder" , "folder" ) ;
if ( string . IsNullOrEmpty ( fname ) )
{
return ; //no name was provided.
}
if ( ! IsFilenameOk ( fname ) ) return ; //new name contains invalid char(s). don't do anything
string relpath = ( CurrentFolder . Path ? ? "" ) + "\\" + fname ;
2018-02-18 17:20:39 +08:00
var rootpath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2018-01-10 11:17:30 +08:00
string fullpath = rootpath + relpath ;
RpfDirectoryEntry newdir = null ;
MainTreeFolder node = null ;
try
{
if ( CurrentFolder . RpfFolder ! = null )
{
2018-01-11 09:10:03 +08:00
if ( ! EnsureRpfValidEncryption ( ) ) return ;
2018-01-10 11:17:30 +08:00
//create new directory entry in the RPF.
newdir = RpfFile . CreateDirectory ( CurrentFolder . RpfFolder , fname ) ;
node = CreateRpfDirTreeFolder ( newdir , relpath , fullpath ) ;
}
else
{
//create a folder in the filesystem.
if ( Directory . Exists ( fullpath ) )
{
throw new Exception ( "Folder " + fullpath + " already exists!" ) ;
}
Directory . CreateDirectory ( fullpath ) ;
node = CreateRootDirTreeFolder ( fname , relpath , fullpath ) ;
}
}
catch ( Exception ex )
{
MessageBox . Show ( "Error creating new folder: " + ex . Message , "Unable to create new folder" ) ;
return ;
}
if ( node ! = null )
{
AddNewFolderTreeNode ( node ) ;
}
}
private void NewRpfArchive ( )
{
if ( CurrentFolder = = null ) return ; //shouldn't happen
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
string fname = Prompt . ShowDialog ( this , "Enter a name for the new archive:" , "Create RPF7 archive" , "new" ) ;
if ( string . IsNullOrEmpty ( fname ) )
{
return ; //no name was provided.
}
if ( ! IsFilenameOk ( fname ) ) return ; //new name contains invalid char(s). don't do anything
if ( ! fname . ToLowerInvariant ( ) . EndsWith ( ".rpf" ) )
{
fname = fname + ".rpf" ; //make sure it ends with .rpf
}
string relpath = ( CurrentFolder . Path ? ? "" ) + "\\" + fname . ToLowerInvariant ( ) ;
RpfEncryption encryption = RpfEncryption . OPEN ; //TODO: select encryption mode
RpfFile newrpf = null ;
try
{
if ( CurrentFolder . RpfFolder ! = null )
{
2018-01-11 09:10:03 +08:00
if ( ! EnsureRpfValidEncryption ( ) ) return ;
2018-01-10 11:17:30 +08:00
//adding a new RPF as a child of another
newrpf = RpfFile . CreateNew ( CurrentFolder . RpfFolder , fname , encryption ) ;
}
else
{
//adding a new RPF in the filesystem
2018-02-18 17:20:39 +08:00
newrpf = RpfFile . CreateNew ( GTAFolder . CurrentGTAFolder , relpath , encryption ) ;
2018-01-10 11:17:30 +08:00
}
}
catch ( Exception ex )
{
MessageBox . Show ( "Error creating archive: " + ex . Message , "Unable to create new archive" ) ;
return ;
}
if ( newrpf ! = null )
{
2018-02-18 17:20:39 +08:00
var node = CreateRpfTreeFolder ( newrpf , newrpf . Path , GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) + newrpf . Path ) ;
2018-01-10 11:17:30 +08:00
RecurseMainTreeViewRPF ( node , AllRpfs ) ;
AddNewFolderTreeNode ( node ) ;
}
}
2017-09-21 18:33:05 +08:00
private void ImportXml ( )
{
if ( ! EditMode ) return ;
2018-01-10 11:17:30 +08:00
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
2018-01-10 12:55:19 +08:00
RpfDirectoryEntry parentrpffldr = CurrentFolder . RpfFolder ;
if ( parentrpffldr = = null )
{
MessageBox . Show ( "No parent RPF folder selected! This shouldn't happen. Refresh the view and try again." ) ;
return ;
}
2018-01-11 09:10:03 +08:00
if ( ! EnsureRpfValidEncryption ( ) ) return ;
2018-01-10 12:55:19 +08:00
OpenFileDialog . Filter = "XML Files|*.xml" ;
if ( OpenFileDialog . ShowDialog ( this ) ! = DialogResult . OK )
{
return ; //canceled
}
try
{
var fpaths = OpenFileDialog . FileNames ;
foreach ( var fpath in fpaths )
{
if ( ! File . Exists ( fpath ) )
{
continue ; //this shouldn't happen...
}
var fi = new FileInfo ( fpath ) ;
var fname = fi . Name ;
var fnamel = fname . ToLowerInvariant ( ) ;
if ( ! fnamel . EndsWith ( ".xml" ) )
{
MessageBox . Show ( fname + ": Not an XML file!" , "Cannot import XML" ) ;
continue ;
}
if ( fnamel . EndsWith ( ".pso.xml" ) )
{
MessageBox . Show ( fname + ": PSO XML import not yet supported." , "Cannot import XML" ) ;
continue ;
}
if ( fnamel . EndsWith ( ".rbf.xml" ) )
{
MessageBox . Show ( fname + ": RBF XML import not yet supported." , "Cannot import XML" ) ;
continue ;
}
fname = fname . Substring ( 0 , fname . Length - 4 ) ;
fnamel = fnamel . Substring ( 0 , fnamel . Length - 4 ) ;
var doc = new XmlDocument ( ) ;
string text = File . ReadAllText ( fpath ) ;
if ( ! string . IsNullOrEmpty ( text ) )
{
doc . LoadXml ( text ) ;
}
var meta = XmlMeta . GetMeta ( doc ) ;
if ( ( meta . DataBlocks ? . Data = = null ) | | ( meta . DataBlocks . Count = = 0 ) )
{
MessageBox . Show ( fname + ": Schema not supported." , "Cannot import XML" ) ;
continue ;
}
byte [ ] data = ResourceBuilder . Build ( meta , 2 ) ; //meta is RSC V:2
foreach ( var exfile in parentrpffldr . Files )
{
if ( exfile . NameLower = = fnamel )
{
//file already exists. delete the existing one first!
//this should probably be optimised to just replace the existing one...
//TODO: investigate along with ReplaceSelected()
RpfFile . DeleteEntry ( exfile ) ;
break ;
}
}
RpfFile . CreateFile ( parentrpffldr , fname , data ) ;
}
}
catch ( Exception ex )
{
MessageBox . Show ( ex . Message , "Unable to import file" ) ;
return ;
}
CurrentFolder . ListItems = null ;
RefreshMainListView ( ) ;
2017-09-21 18:33:05 +08:00
}
private void ImportRaw ( )
{
if ( ! EditMode ) return ;
2018-01-10 11:17:30 +08:00
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
RpfDirectoryEntry parentrpffldr = CurrentFolder . RpfFolder ;
if ( parentrpffldr = = null )
{
MessageBox . Show ( "No parent RPF folder selected! This shouldn't happen. Refresh the view and try again." ) ;
return ;
}
2018-01-11 09:10:03 +08:00
if ( ! EnsureRpfValidEncryption ( ) ) return ;
2018-01-10 12:55:19 +08:00
OpenFileDialog . Filter = string . Empty ;
2018-01-10 11:17:30 +08:00
if ( OpenFileDialog . ShowDialog ( this ) ! = DialogResult . OK )
{
return ; //canceled
}
try
{
var fpaths = OpenFileDialog . FileNames ;
foreach ( var fpath in fpaths )
{
if ( ! File . Exists ( fpath ) )
{
continue ; //this shouldn't happen...
}
var fi = new FileInfo ( fpath ) ;
var fname = fi . Name ;
var fnamel = fname . ToLowerInvariant ( ) ;
if ( fi . Length > 0x3FFFFFFF )
{
MessageBox . Show ( "File " + fname + " is too big! Max 1GB supported." , "Unable to import file" ) ;
continue ;
}
2018-01-10 12:55:19 +08:00
byte [ ] data = File . ReadAllBytes ( fpath ) ;
2018-01-10 11:17:30 +08:00
foreach ( var exfile in parentrpffldr . Files )
{
if ( exfile . NameLower = = fnamel )
{
//file already exists. delete the existing one first!
//this should probably be optimised to just replace the existing one...
//TODO: investigate along with ReplaceSelected()
RpfFile . DeleteEntry ( exfile ) ;
break ;
}
}
2018-01-11 09:10:03 +08:00
var entry = RpfFile . CreateFile ( parentrpffldr , fname , data ) ;
var newrpf = parentrpffldr . File ? . FindChildArchive ( entry ) ;
if ( newrpf ! = null )
{
//an RPF file was imported. add its structure to the UI!
2018-02-18 17:20:39 +08:00
var rootpath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2018-01-11 09:10:03 +08:00
var tnf = CreateRpfTreeFolder ( newrpf , newrpf . Path , rootpath + newrpf . Path ) ;
2018-01-11 11:06:48 +08:00
if ( CurrentFolder . Children ! = null )
2018-01-11 09:10:03 +08:00
{
2018-01-11 11:06:48 +08:00
//make sure any existing (replaced!) one is removed first!
2018-01-11 09:10:03 +08:00
foreach ( var child in CurrentFolder . Children )
{
if ( child . Path = = tnf . Path )
{
CurrentFolder . Children . Remove ( child ) ;
child . TreeNode . Remove ( ) ;
break ;
}
}
}
CurrentFolder . AddChildToHierarchy ( tnf ) ;
RecurseMainTreeViewRPF ( tnf , AllRpfs ) ;
RecurseAddMainTreeViewNodes ( tnf , CurrentFolder . TreeNode ) ;
}
2018-01-10 11:17:30 +08:00
}
}
catch ( Exception ex )
{
MessageBox . Show ( ex . Message , "Unable to import file" ) ;
return ;
}
CurrentFolder . ListItems = null ;
RefreshMainListView ( ) ;
2017-09-21 18:33:05 +08:00
}
private void CopySelected ( )
{
//only really for edit mode...
MessageBox . Show ( "CopySelected TODO!" ) ;
}
private void CopyPath ( )
{
if ( MainListView . SelectedIndices . Count = = 0 )
{
2018-02-18 17:20:39 +08:00
Clipboard . SetText ( CurrentFolder ? . FullPath ? ? GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ) ;
2017-09-21 18:33:05 +08:00
}
else if ( MainListView . SelectedIndices . Count = = 1 )
{
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) return ;
var f = CurrentFiles [ idx ] ;
if ( f . FullPath ! = null )
{
Clipboard . SetText ( f . FullPath ) ;
}
}
else
{
StringBuilder sb = new StringBuilder ( ) ;
foreach ( int idx in MainListView . SelectedIndices )
{
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) return ;
var f = CurrentFiles [ idx ] ;
if ( f . FullPath ! = null )
{
sb . AppendLine ( f . FullPath ) ;
}
}
Clipboard . SetText ( sb . ToString ( ) ) ;
}
}
private void CopyPath ( string path )
{
Clipboard . SetText ( path ) ;
}
private void CopyFileList ( )
{
StringBuilder sb = new StringBuilder ( ) ;
if ( CurrentFiles ! = null )
{
foreach ( var file in CurrentFiles )
{
sb . AppendLine ( file . Name ) ;
}
}
Clipboard . SetText ( sb . ToString ( ) ) ;
}
private void RenameSelected ( )
{
if ( ! EditMode ) return ;
2018-01-12 12:19:32 +08:00
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
2018-01-10 11:17:30 +08:00
if ( MainListView . SelectedIndices . Count ! = 1 ) return ;
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( CurrentFiles ! = null ) & & ( CurrentFiles . Count > idx ) )
{
var item = CurrentFiles [ idx ] ;
string newname = Prompt . ShowDialog ( this , "Enter the new name for this item:" , "Rename item" , item . Name ) ;
if ( ! string . IsNullOrEmpty ( newname ) )
{
RenameItem ( item , newname ) ;
}
}
}
private void RenameItem ( MainListItem item , string newname )
{
if ( ! EditMode ) return ;
if ( item . Name = = newname ) return ;
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
if ( ! IsFilenameOk ( newname ) ) return ; //new name contains invalid char(s). don't do anything
RpfFile file = item . Folder ? . RpfFile ;
RpfEntry entry = item . GetRpfEntry ( ) ;
try
{
if ( file ! = null )
{
//updates all items in the RPF with the new path - no actual file changes made here
RpfFile . RenameArchive ( file , newname ) ;
}
if ( entry ! = null )
{
2018-01-11 09:10:03 +08:00
if ( ! EnsureRpfValidEncryption ( ) ) return ;
2018-01-10 11:17:30 +08:00
//renaming an entry in an RPF
RpfFile . RenameEntry ( entry , newname ) ;
}
else
{
//renaming a filesystem item...
var dirinfo = new DirectoryInfo ( item . FullPath ) ;
var newpath = Path . Combine ( dirinfo . Parent . FullName , newname ) ;
if ( item . FullPath . ToLowerInvariant ( ) = = newpath . ToLowerInvariant ( ) )
{
return ; //filesystem tends to be case-insensitive... paths are the same
}
if ( ( item . Folder ! = null ) & & ( item . Folder . RpfFile = = null ) )
{
//renaming a filesystem folder...
Directory . Move ( item . FullPath , newpath ) ;
}
else
{
//renaming a filesystem file...
File . Move ( item . FullPath , newpath ) ;
}
}
if ( item . Folder ! = null )
{
RenameTreeFolder ( item . Folder , newname ) ;
}
RenameListItem ( item , newname ) ;
}
catch ( Exception ex )
{
MessageBox . Show ( "Error renaming " + item . Path + ": " + ex . Message , "Unable to rename item" ) ;
return ;
}
2017-09-21 18:33:05 +08:00
}
private void ReplaceSelected ( )
{
if ( ! EditMode ) return ;
2018-01-10 11:17:30 +08:00
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
if ( MainListView . SelectedIndices . Count ! = 1 ) return ;
MessageBox . Show ( "ReplaceSelected TODO..." ) ;
//delete the selected items, and replace with... choose
2018-01-11 09:10:03 +08:00
//if (!EnsureRpfEncryptionType()) return;
2017-09-21 18:33:05 +08:00
}
private void DeleteSelected ( )
{
if ( ! EditMode ) return ;
2018-01-10 11:17:30 +08:00
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
if ( MainListView . SelectedIndices . Count < = 0 ) return ;
//if (MainListView.SelectedIndices.Count == 1) //is confirmation always really necessary?
//{
// var item = CurrentFiles[MainListView.SelectedIndices[0]];
// if (MessageBox.Show("Are you sure you want to permantly delete " + item.Name + "?\nThis cannot be undone.", "Confirm delete", MessageBoxButtons.YesNo) != DialogResult.Yes)
// {
// return;
// }
//}
//else
//{
// if (MessageBox.Show("Are you sure you want to permantly delete " + MainListView.SelectedIndices.Count.ToString() + " items?\nThis cannot be undone.", "Confirm delete", MessageBoxButtons.YesNo) != DialogResult.Yes)
// {
// return;
// }
//}
var delitems = new List < MainListItem > ( ) ;
foreach ( int idx in MainListView . SelectedIndices )
{
if ( ( idx < 0 ) | | ( idx > = CurrentFiles . Count ) ) return ;
var f = CurrentFiles [ idx ] ; //this could change when deleting.. so need to use the temp list
delitems . Add ( f ) ;
}
foreach ( var f in delitems )
{
DeleteItem ( f ) ;
}
}
private void DeleteItem ( MainListItem item )
{
try
{
2018-01-12 12:19:32 +08:00
if ( item . Folder ? . RpfFile ! = null )
{
//confirm deletion of RPF archives, just to be friendly.
if ( MessageBox . Show ( "Are you sure you want to delete this archive?\n" + item . Path , "Confirm delete archive" , MessageBoxButtons . YesNo ) ! = DialogResult . Yes )
{
return ;
}
}
2018-01-10 11:17:30 +08:00
var parent = item . Parent ;
if ( parent . RpfFolder ! = null )
{
//delete an item in an RPF.
2018-01-11 09:10:03 +08:00
if ( ! EnsureRpfValidEncryption ( ) ) return ;
2018-01-10 11:17:30 +08:00
RpfEntry entry = item . GetRpfEntry ( ) ;
RpfFile . DeleteEntry ( entry ) ;
}
else
{
//delete an item in the filesystem.
if ( ( item . Folder ! = null ) & & ( item . Folder . RpfFile = = null ) )
{
Directory . Delete ( item . FullPath ) ;
}
else
{
File . Delete ( item . FullPath ) ;
}
}
if ( item . Folder ! = null )
{
RemoveTreeFolder ( item . Folder ) ;
}
RemoveListItem ( item ) ;
}
catch ( Exception ex )
{
MessageBox . Show ( "Error deleting " + item . Path + ": " + ex . Message , "Unable to delete " + item . Name ) ;
return ;
}
2017-09-21 18:33:05 +08:00
}
2018-01-12 12:19:32 +08:00
private void DefragmentSelected ( )
{
if ( ! EditMode ) return ;
if ( CurrentFolder ? . IsSearchResults ? ? false ) return ;
if ( MainListView . SelectedIndices . Count ! = 1 )
{
MessageBox . Show ( "Can only defragment one item at a time. Please have only one item selected." ) ;
return ;
}
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( CurrentFiles = = null ) | | ( CurrentFiles . Count < = idx ) )
{
return ;
}
var item = CurrentFiles [ idx ] ;
var rpf = item . Folder ? . RpfFile ;
if ( rpf = = null )
{
MessageBox . Show ( "Can only defragment RPF archives!" ) ;
return ;
}
Form form = new Form ( ) {
Width = 450 ,
Height = 250 ,
FormBorderStyle = FormBorderStyle . FixedDialog ,
Text = "Defragment RPF Archive - CodeWalker by dexyfex" ,
StartPosition = FormStartPosition . CenterParent ,
MaximizeBox = false ,
MinimizeBox = false
} ;
var addCtrl = new Func < Control , Control > ( c = > { form . Controls . Add ( c ) ; return c ; } ) ;
var addLabel = new Func < int , string , Control > ( ( y , t ) = > {
return addCtrl ( new Label ( ) { Left = 30 , Top = y , Width = 370 , Height = 20 , Text = t } ) ;
} ) ;
var rpfNameLabel = addLabel ( 20 , "Archive: " + rpf . Path ) ;
var curSizeLabel = addLabel ( 40 , string . Empty ) ;
var newSizeLabel = addLabel ( 60 , string . Empty ) ;
var redSizeLabel = addLabel ( 80 , string . Empty ) ;
var statusLabel = addLabel ( 110 , string . Empty ) ;
var progressBar = addCtrl ( new ProgressBar ( ) { Left = 30 , Top = 130 , Width = 370 , Height = 20 , Minimum = 0 , Maximum = 1000 , MarqueeAnimationSpeed = 50 } ) as ProgressBar ;
var beginButton = addCtrl ( new Button ( ) { Text = "Begin Defragment" , Left = 30 , Top = 170 , Width = 120 } ) as Button ;
var closeButton = addCtrl ( new Button ( ) { Text = "Close" , Left = 320 , Top = 170 , Width = 80 } ) as Button ;
var inProgress = false ;
var updateProgress = new Action < string , float > ( ( s , p ) = > { form . Invoke ( new Action ( ( ) = > {
statusLabel . Text = s ;
progressBar . Value = Math . Max ( 0 , Math . Min ( ( int ) ( p * 1000 ) , 1000 ) ) ; //p in range 0..1
} ) ) ; } ) ;
var updateSizeLabels = new Action < bool > ( ( init ) = > {
var curSize = rpf . FileSize ;
var newSize = rpf . GetDefragmentedFileSize ( ) ;
var redSize = curSize - newSize ;
curSizeLabel . Text = "Archive current size: " + TextUtil . GetBytesReadable ( curSize ) ;
newSizeLabel . Text = "Defragmented size: " + TextUtil . GetBytesReadable ( newSize ) ;
redSizeLabel . Text = "Size reduction: " + TextUtil . GetBytesReadable ( redSize ) ;
if ( init ) statusLabel . Text = ( redSize < = 0 ) ? "Defragmentation not required." : "Ready to defragment." ;
} ) ;
var enableUi = new Action < bool > ( enable = > { form . Invoke ( new Action ( ( ) = > {
beginButton . Enabled = enable ;
closeButton . Enabled = enable ;
} ) ) ; } ) ;
var defragment = new Action ( ( ) = > { Task . Run ( ( ) = > {
if ( inProgress ) return ;
if ( ! EnsureRpfValidEncryption ( rpf ) ) return ;
inProgress = true ;
enableUi ( false ) ;
RpfFile . Defragment ( rpf , updateProgress ) ;
updateProgress ( "Defragment complete." , 1.0f ) ;
enableUi ( true ) ;
form . Invoke ( new Action ( ( ) = > { updateSizeLabels ( false ) ; } ) ) ;
inProgress = false ;
} ) ; } ) ;
updateSizeLabels ( true ) ;
beginButton . Click + = ( sender , e ) = > { defragment ( ) ; } ;
closeButton . Click + = ( sender , e ) = > { form . Close ( ) ; } ;
form . FormClosing + = ( s , e ) = > { e . Cancel = inProgress ; } ;
form . ShowDialog ( this ) ;
RefreshMainListView ( ) ;
}
2017-09-21 18:33:05 +08:00
private void SelectAll ( )
{
MainListView . SelectAllItems ( ) ;
UpdateSelectionUI ( ) ;
}
private void ShowInExplorer ( string path )
{
try
{
Process . Start ( "explorer" , "/select, \"" + path + "\"" ) ;
}
catch ( Exception ex )
{
UpdateErrorLog ( ex . ToString ( ) ) ;
}
}
private void OpenFileLocation ( )
{
var ind = - 1 ;
if ( MainListView . SelectedIndices . Count = = 1 )
{
ind = MainListView . SelectedIndices [ 0 ] ;
}
if ( ( CurrentFiles ! = null ) & & ( CurrentFiles . Count > ind ) )
{
var file = CurrentFiles [ ind ] ;
var path = file . Path . Replace ( '/' , '\\' ) ;
var bsind = path . LastIndexOf ( '\\' ) ;
if ( bsind > 0 )
{
path = path . Substring ( 0 , bsind ) ;
}
Navigate ( path ) ;
}
}
private void SetView ( View v )
{
MainListView . View = v ;
foreach ( ToolStripMenuItem menu in ViewMenu . DropDownItems )
{
menu . Checked = false ;
}
switch ( v )
{
case System . Windows . Forms . View . LargeIcon :
ViewLargeIconsMenu . Checked = true ;
break ;
case System . Windows . Forms . View . SmallIcon :
ViewSmallIconsMenu . Checked = true ;
break ;
case System . Windows . Forms . View . List :
ViewListMenu . Checked = true ;
break ;
case System . Windows . Forms . View . Details :
ViewDetailsMenu . Checked = true ;
break ;
}
}
private void ExploreForm_Load ( object sender , EventArgs e )
{
Init ( ) ;
}
private void MainTreeView_AfterSelect ( object sender , TreeViewEventArgs e )
{
var prev = CurrentFolder ;
CurrentFolder = e . Node ? . Tag as MainTreeFolder ;
LocationTextBox . Text = CurrentFolder ? . Path ? ? "" ;
if ( ! string . IsNullOrEmpty ( CurrentFolder ? . SearchTerm ) )
{
SearchTextBox . Text = CurrentFolder . SearchTerm ;
}
RefreshMainListView ( ) ;
NavigateComplete ( prev ) ;
}
private void MainTreeView_MouseUp ( object sender , MouseEventArgs e )
{
if ( e . Button = = MouseButtons . Right )
{
// Point where the mouse is clicked.
Point p = new Point ( e . X , e . Y ) ;
// Get the node that the user has clicked.
TreeNode node = MainTreeView . GetNodeAt ( p ) ;
if ( node ! = null )
{
// Select the node the user has clicked.
// The node appears selected until the menu is displayed on the screen.
//var oldNode = MainTreeView.SelectedNode;
MainTreeView . SelectedNode = node ;
ShowTreeContextMenu ( node , p ) ;
// Highlight the selected node.
//MainTreeView.SelectedNode = oldNode;
}
}
}
private void MainListView_RetrieveVirtualItem ( object sender , RetrieveVirtualItemEventArgs e )
{
var lvi = new ListViewItem ( ) ;
if ( ( CurrentFiles ! = null ) & & ( e . ItemIndex < CurrentFiles . Count ) )
{
var file = CurrentFiles [ e . ItemIndex ] ;
var fld = file . Folder ;
lvi . Tag = file ;
lvi . Text = file . Name ;
lvi . ToolTipText = file . Path ;
lvi . ImageIndex = file . ImageIndex ;
lvi . SubItems . Add ( file . FileTypeText ) ; //type column
lvi . SubItems . Add ( file . FileSizeText ) ; //size column
lvi . SubItems . Add ( file . Attributes ) ; //attributes column
lvi . SubItems . Add ( file . Path ) ; //path column
}
e . Item = lvi ;
}
private void MainListView_ColumnClick ( object sender , ColumnClickEventArgs e )
{
var newdir = SortDirection ;
var idx = e . Column ;
if ( idx = = SortColumnIndex )
{
switch ( SortDirection )
{
case SortOrder . None : newdir = SortOrder . Ascending ; break ;
case SortOrder . Ascending : newdir = SortOrder . Descending ; break ;
case SortOrder . Descending : newdir = SortOrder . Ascending ; break ;
}
}
SortMainListView ( idx , newdir ) ;
}
private void MainListView_MouseUp ( object sender , MouseEventArgs e )
{
if ( e . Button = = MouseButtons . Right )
{
MainListItem item = null ;
if ( ( MainListView . FocusedItem ! = null ) & & ( MainListView . FocusedItem . Bounds . Contains ( e . Location ) ) )
{
item = MainListView . FocusedItem . Tag as MainListItem ;
}
ShowListContextMenu ( item ) ;
}
UpdateSelectionUI ( ) ; //need to use this instead of SelectedIndexChanged because of shift-click bug :/
}
private void MainListView_KeyDown ( object sender , KeyEventArgs e )
{
var ctrl = ( e . Control & & ! e . Shift ) ;
var ctrlshft = ( e . Control & & e . Shift ) ;
var shft = ( e . Shift & & ! e . Control ) ;
switch ( e . KeyCode )
{
case Keys . P :
if ( ctrl ) ViewSelected ( ) ;
break ;
case Keys . H :
if ( ctrl ) ViewSelectedHex ( ) ;
break ;
case Keys . S :
if ( ctrl ) ExportXml ( ) ;
break ;
case Keys . E :
if ( ctrlshft ) ExtractAll ( ) ;
else if ( ctrl ) ExtractRaw ( ) ;
break ;
case Keys . Insert :
if ( MainListView . SelectedIndices . Count = = 1 )
{
if ( shft ) ReplaceSelected ( ) ;
}
else
{
if ( shft ) ImportXml ( ) ;
else if ( ! ctrl ) ImportRaw ( ) ;
}
break ;
case Keys . C :
if ( ctrlshft ) CopyPath ( ) ;
else if ( ctrl ) CopySelected ( ) ;
break ;
case Keys . F2 :
RenameSelected ( ) ;
break ;
case Keys . Delete :
if ( shft ) DeleteSelected ( ) ;
break ;
case Keys . A :
if ( ctrl ) SelectAll ( ) ;
break ;
}
}
private void MainListView_KeyUp ( object sender , KeyEventArgs e )
{
UpdateSelectionUI ( ) ; //need to use this instead of SelectedIndexChanged because of shift-click bug :/
}
private void MainListView_ItemActivate ( object sender , EventArgs e )
{
if ( MainListView . SelectedIndices . Count = = 1 )
{
var idx = MainListView . SelectedIndices [ 0 ] ;
if ( ( idx > = 0 ) & & ( idx < CurrentFiles . Count ) )
{
var file = CurrentFiles [ idx ] ;
if ( file . Folder ! = null )
{
Navigate ( file . Folder ) ;
}
else
{
//a file was activated. open it... (or, perform "default action"?)
View ( file ) ;
}
}
}
else
{
//how to activate multiple items? open files?
}
}
2018-01-10 11:17:30 +08:00
private void MainListView_AfterLabelEdit ( object sender , LabelEditEventArgs e )
{
if ( ( CurrentFiles ! = null ) & & ( CurrentFiles . Count > e . Item ) & & ( ! string . IsNullOrEmpty ( e . Label ) ) )
{
RenameItem ( CurrentFiles [ e . Item ] , e . Label ) ;
}
}
2017-09-21 18:33:05 +08:00
private void BackButton_ButtonClick ( object sender , EventArgs e )
{
GoBack ( ) ;
}
private void BackListButton_Click ( object sender , EventArgs e )
{
var step = ( sender as ToolStripItem ) ? . Tag as MainTreeFolder ;
if ( step = = null ) return ;
GoBack ( step ) ;
}
private void ForwardButton_ButtonClick ( object sender , EventArgs e )
{
GoForward ( ) ;
}
private void ForwardListButton_Click ( object sender , EventArgs e )
{
var step = ( sender as ToolStripItem ) ? . Tag as MainTreeFolder ;
if ( step = = null ) return ;
GoForward ( step ) ;
}
private void UpButton_ButtonClick ( object sender , EventArgs e )
{
GoUp ( ) ;
}
private void UpListButton_Click ( object sender , EventArgs e )
{
var step = ( sender as ToolStripItem ) ? . Tag as MainTreeFolder ;
if ( step = = null ) return ;
GoUp ( step ) ;
}
private void LocationTextBox_KeyPress ( object sender , KeyPressEventArgs e )
{
if ( e . KeyChar = = 13 )
{
Navigate ( LocationTextBox . Text ) ;
e . Handled = true ;
}
}
private void LocationTextBox_Enter ( object sender , EventArgs e )
{
BeginInvoke ( new Action ( ( ) = > LocationTextBox . SelectAll ( ) ) ) ;
}
private void GoButton_Click ( object sender , EventArgs e )
{
Navigate ( LocationTextBox . Text ) ;
}
private void RefreshButton_Click ( object sender , EventArgs e )
{
Task . Run ( ( ) = >
{
UpdateStatus ( "Scanning..." ) ;
RefreshMainTreeView ( ) ;
UpdateStatus ( "Scan complete." ) ;
} ) ;
}
2018-01-10 11:17:30 +08:00
private void EditModeButton_Click ( object sender , EventArgs e )
{
EnableEditMode ( ! EditMode ) ;
}
2017-09-21 18:33:05 +08:00
private void SearchTextBox_KeyPress ( object sender , KeyPressEventArgs e )
{
if ( e . KeyChar = = 13 )
{
SearchButton_ButtonClick ( sender , e ) ;
e . Handled = true ;
}
}
private void SearchTextBox_Enter ( object sender , EventArgs e )
{
BeginInvoke ( new Action ( ( ) = > SearchTextBox . SelectAll ( ) ) ) ;
}
private void SearchButton_ButtonClick ( object sender , EventArgs e )
{
if ( SearchGlobalButton . Checked )
{
Search ( SearchTextBox . Text ) ;
}
else
{
Filter ( SearchTextBox . Text ) ;
}
}
private void SearchGlobalButton_Click ( object sender , EventArgs e )
{
Search ( SearchTextBox . Text ) ;
}
private void SearchFilterButton_Click ( object sender , EventArgs e )
{
Filter ( SearchTextBox . Text ) ;
}
private void TreeContextCopyPathMenu_Click ( object sender , EventArgs e )
{
var f = MainTreeView . SelectedNode ? . Tag as MainTreeFolder ;
if ( f ! = null )
{
CopyPath ( f . FullPath ) ;
}
}
private void TreeContextWinExplorerMenu_Click ( object sender , EventArgs e )
{
var folder = MainTreeView . SelectedNode ? . Tag as MainTreeFolder ;
2018-02-18 17:20:39 +08:00
var path = folder ? . FullPath ? ? GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2017-09-21 18:33:05 +08:00
ShowInExplorer ( path ) ;
}
private void TreeContextExpandMenu_Click ( object sender , EventArgs e )
{
if ( MainTreeView . SelectedNode ! = null )
{
MainTreeView . SelectedNode . Expand ( ) ;
}
}
private void TreeContextCollapseMenu_Click ( object sender , EventArgs e )
{
if ( MainTreeView . SelectedNode ! = null )
{
MainTreeView . SelectedNode . Collapse ( ) ;
}
}
private void TreeContextCollapseAllMenu_Click ( object sender , EventArgs e )
{
var seln = MainTreeView . SelectedNode ;
foreach ( TreeNode root in MainTreeView . Nodes )
{
foreach ( TreeNode n in root . Nodes )
{
n . Collapse ( false ) ; //collapse the first level nodes (not the roots)
}
}
if ( MainTreeView . SelectedNode ! = seln )
{
TreeViewEventArgs tve = new TreeViewEventArgs ( MainTreeView . SelectedNode ) ;
MainTreeView_AfterSelect ( MainTreeView , tve ) ; //for some reason, this event doesn't get raised when the selected node changes here
}
}
private void ListContextViewMenu_Click ( object sender , EventArgs e )
{
ViewSelected ( ) ;
}
private void ListContextViewHexMenu_Click ( object sender , EventArgs e )
{
ViewSelectedHex ( ) ;
}
private void ListContextExportXmlMenu_Click ( object sender , EventArgs e )
{
ExportXml ( ) ;
}
private void ListContextExtractRawMenu_Click ( object sender , EventArgs e )
{
ExtractRaw ( ) ;
}
2017-09-27 00:59:39 +08:00
private void ListContextExtractUncompressedMenu_Click ( object sender , EventArgs e )
{
ExtractUncompressed ( ) ;
}
2017-09-21 18:33:05 +08:00
private void ListContextExtractAllMenu_Click ( object sender , EventArgs e )
{
ExtractAll ( ) ;
}
2018-01-10 11:17:30 +08:00
private void ListContextNewFolderMenu_Click ( object sender , EventArgs e )
{
NewFolder ( ) ;
}
private void ListContextNewRpfArchiveMenu_Click ( object sender , EventArgs e )
{
NewRpfArchive ( ) ;
}
2017-09-21 18:33:05 +08:00
private void ListContextImportXmlMenu_Click ( object sender , EventArgs e )
{
ImportXml ( ) ;
}
private void ListContextImportRawMenu_Click ( object sender , EventArgs e )
{
ImportRaw ( ) ;
}
private void ListContextCopyMenu_Click ( object sender , EventArgs e )
{
CopySelected ( ) ;
}
private void ListContextCopyPathMenu_Click ( object sender , EventArgs e )
{
CopyPath ( ) ;
}
private void ListContextCopyFileListMenu_Click ( object sender , EventArgs e )
{
CopyFileList ( ) ;
}
private void ListContextOpenFileLocationMenu_Click ( object sender , EventArgs e )
{
OpenFileLocation ( ) ;
}
private void ListContextRenameMenu_Click ( object sender , EventArgs e )
{
RenameSelected ( ) ;
}
private void ListContextReplaceMenu_Click ( object sender , EventArgs e )
{
ReplaceSelected ( ) ;
}
private void ListContextDeleteMenu_Click ( object sender , EventArgs e )
{
DeleteSelected ( ) ;
}
2018-01-12 12:19:32 +08:00
private void ListContextDefragmentMenu_Click ( object sender , EventArgs e )
{
DefragmentSelected ( ) ;
}
2017-09-21 18:33:05 +08:00
private void ListContextSelectAllMenu_Click ( object sender , EventArgs e )
{
SelectAll ( ) ;
}
private void FileExitMenu_Click ( object sender , EventArgs e )
{
Close ( ) ;
}
private void EditViewMenu_Click ( object sender , EventArgs e )
{
ViewSelected ( ) ;
}
private void EditViewHexMenu_Click ( object sender , EventArgs e )
{
ViewSelectedHex ( ) ;
}
private void EditExportXmlMenu_Click ( object sender , EventArgs e )
{
ExportXml ( ) ;
}
private void EditExtractRawMenu_Click ( object sender , EventArgs e )
{
ExtractRaw ( ) ;
}
private void EditExtractAllMenu_Click ( object sender , EventArgs e )
{
ExtractAll ( ) ;
}
private void EditImportXmlMenu_Click ( object sender , EventArgs e )
{
ImportXml ( ) ;
}
private void EditImportRawMenu_Click ( object sender , EventArgs e )
{
ImportRaw ( ) ;
}
private void EditCopyMenu_Click ( object sender , EventArgs e )
{
CopySelected ( ) ;
}
private void EditCopyPathMenu_Click ( object sender , EventArgs e )
{
CopyPath ( ) ;
}
private void EditCopyFileListMenu_Click ( object sender , EventArgs e )
{
CopyFileList ( ) ;
}
private void EditRenameMenu_Click ( object sender , EventArgs e )
{
RenameSelected ( ) ;
}
private void EditReplaceMenu_Click ( object sender , EventArgs e )
{
ReplaceSelected ( ) ;
}
private void EditDeleteMenu_Click ( object sender , EventArgs e )
{
DeleteSelected ( ) ;
}
private void EditSelectAllMenu_Click ( object sender , EventArgs e )
{
SelectAll ( ) ;
}
private void ViewLargeIconsMenu_Click ( object sender , EventArgs e )
{
SetView ( System . Windows . Forms . View . LargeIcon ) ;
}
private void ViewSmallIconsMenu_Click ( object sender , EventArgs e )
{
SetView ( System . Windows . Forms . View . SmallIcon ) ;
}
private void ViewListMenu_Click ( object sender , EventArgs e )
{
SetView ( System . Windows . Forms . View . List ) ;
}
private void ViewDetailsMenu_Click ( object sender , EventArgs e )
{
SetView ( System . Windows . Forms . View . Details ) ;
}
private void ToolsOptionsMenu_Click ( object sender , EventArgs e )
{
MessageBox . Show ( "Options TODO!" ) ;
}
private void ToolsRpfBrowserMenu_Click ( object sender , EventArgs e )
{
BrowseForm f = new BrowseForm ( ) ;
f . Show ( this ) ;
}
2017-12-24 20:36:18 +08:00
private void ToolsBinSearchMenu_Click ( object sender , EventArgs e )
{
BinarySearchForm f = new BinarySearchForm ( FileCache ) ;
f . Show ( this ) ;
}
2017-09-21 18:33:05 +08:00
}
public class MainTreeFolder
{
public string Name { get ; set ; }
public string Path { get ; set ; }
public string FullPath { get ; set ; }
public RpfFile RpfFile { get ; set ; }
public RpfDirectoryEntry RpfFolder { get ; set ; }
public List < string > Files { get ; set ; }
public MainTreeFolder Parent { get ; set ; }
public List < MainTreeFolder > Children { get ; set ; }
public List < MainListItem > ListItems { get ; set ; }
2018-01-10 11:17:30 +08:00
public TreeNode TreeNode { get ; set ; }
2017-09-21 18:33:05 +08:00
public bool IsSearchResults { get ; set ; }
public string SearchTerm { get ; set ; }
public void AddFile ( string file )
{
if ( Files = = null ) Files = new List < string > ( ) ;
Files . Add ( file ) ;
}
public void AddChild ( MainTreeFolder child )
{
if ( Children = = null ) Children = new List < MainTreeFolder > ( ) ;
Children . Add ( child ) ;
child . Parent = this ;
}
public void AddChildToHierarchy ( MainTreeFolder child )
{
var relpath = child . Path . Replace ( Path + "\\" , "" ) ;
var idx = relpath . IndexOf ( '\\' ) ;
var lidx = 0 ;
var parent = this ;
while ( idx > 0 )
{
var pname = relpath . Substring ( lidx , idx - lidx ) ;
if ( parent . Children = = null ) break ;
foreach ( var tc in parent . Children )
{
if ( tc . Name = = pname )
{
parent = tc ;
break ;
}
}
lidx = idx + 1 ;
if ( lidx > = relpath . Length ) break ;
idx = relpath . IndexOf ( '\\' , lidx ) ;
}
parent . AddChild ( child ) ;
}
public List < MainListItem > GetListItems ( )
{
if ( ListItems = = null )
{
ListItems = new List < MainListItem > ( ) ;
2018-02-18 17:20:39 +08:00
var rootpath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2017-09-21 18:33:05 +08:00
if ( Children ! = null )
{
foreach ( var child in Children )
{
ListItems . Add ( new MainListItem ( child ) ) ;
}
}
if ( Files ! = null )
{
foreach ( var file in Files )
{
ListItems . Add ( new MainListItem ( file , rootpath , this ) ) ;
}
}
if ( ( RpfFolder ! = null ) & & ( RpfFolder . Files ! = null ) )
{
foreach ( var file in RpfFolder . Files )
{
if ( file . NameLower . EndsWith ( ".rpf" ) ) continue ; //RPF files are already added..
ListItems . Add ( new MainListItem ( file , rootpath , this ) ) ;
}
}
}
return ListItems ;
}
public int GetItemCount ( )
{
int ic = 0 ;
if ( Children ! = null ) ic + = Children . Count ;
if ( Files ! = null ) ic + = Files . Count ;
if ( ( RpfFolder ! = null ) & & ( RpfFolder . Files ! = null ) )
{
foreach ( var file in RpfFolder . Files )
{
if ( file . NameLower . EndsWith ( ".rpf" ) ) continue ; //RPF files are already added..
ic + + ;
}
}
return ic ;
}
public string GetToolText ( )
{
if ( string . IsNullOrEmpty ( Path ) ) return Name ;
return Path ;
}
public int Search ( string term , ExploreForm form )
{
int resultcount = 0 ;
//if (!form.Searching) return resultcount;
form . UpdateStatus ( "Searching " + Path + "..." ) ;
if ( Name . ToLowerInvariant ( ) . Contains ( term ) )
{
form . AddSearchResult ( new MainListItem ( this ) ) ;
resultcount + + ;
}
2018-02-18 17:20:39 +08:00
var rootpath = GTAFolder . GetCurrentGTAFolderWithTrailingSlash ( ) ;
2017-09-21 18:33:05 +08:00
if ( Files ! = null )
{
foreach ( var file in Files )
{
//if (!form.Searching) return resultcount;
var fi = new FileInfo ( file ) ;
if ( fi . Name . ToLowerInvariant ( ) . Contains ( term ) )
{
form . AddSearchResult ( new MainListItem ( file , rootpath , this ) ) ;
resultcount + + ;
}
}
}
if ( ( RpfFolder ! = null ) & & ( RpfFolder . Files ! = null ) )
{
foreach ( var file in RpfFolder . Files )
{
//if (!form.Searching) return resultcount;
if ( file . NameLower . EndsWith ( ".rpf" ) ) continue ; //don't search rpf files..
if ( file . NameLower . Contains ( term ) )
{
form . AddSearchResult ( new MainListItem ( file , rootpath , this ) ) ;
resultcount + + ;
}
}
}
if ( Children ! = null )
{
foreach ( var child in Children )
{
//if (!form.Searching) return resultcount;
resultcount + = child . Search ( term , form ) ;
}
}
form . AddSearchResult ( null ) ;
return resultcount ;
}
public override string ToString ( )
{
return Name ;
}
}
public class MainListItem
{
public string Name { get ; set ; }
public MainTreeFolder Parent { get ; set ; }
public MainTreeFolder Folder { get ; set ; }
public RpfFileEntry File { get ; set ; }
public string Path { get ; set ; }
public string FullPath { get ; set ; }
public FileTypeInfo FileType { get ; set ; }
public string FileTypeText { get ; set ; }
public long FileSize { get ; set ; }
public string FileSizeText { get ; set ; }
public string Attributes { get ; set ; }
public int ImageIndex { get ; set ; }
public MainListItem ( MainTreeFolder f )
{
Parent = f ? . Parent ;
Folder = f ;
Name = f . Name ;
Path = f . Path ;
FullPath = f . FullPath ;
}
public MainListItem ( string file , string rootpath , MainTreeFolder parent )
{
Parent = parent ;
Name = new FileInfo ( file ) . Name ;
Path = file . Replace ( rootpath , "" ) ;
FullPath = file ;
}
public MainListItem ( RpfFileEntry file , string rootpath , MainTreeFolder parent )
{
Parent = parent ;
File = file ;
Name = file . Name ;
Path = file . Path ;
FullPath = rootpath + file . Path ;
}
public override string ToString ( )
{
return Name ;
}
public void CacheDetails ( ExploreForm form )
{
var fld = Folder ;
FileType = form . GetFileType ( Name ) ;
FileTypeText = FileType . Name ;
FileSizeText = "" ;
Attributes = "" ;
ImageIndex = FileType . ImageIndex ;
if ( File ! = null )
{
FileSize = File . GetFileSize ( ) ;
FileSizeText = TextUtil . GetBytesReadable ( FileSize ) ;
if ( File is RpfResourceFileEntry )
{
var resf = File as RpfResourceFileEntry ;
Attributes + = "Resource [V." + resf . Version . ToString ( ) + "]" ;
}
if ( File . IsEncrypted )
{
if ( Attributes . Length > 0 ) Attributes + = ", " ;
Attributes + = "Encrypted" ;
}
}
else if ( fld ! = null )
{
if ( fld . RpfFile ! = null )
{
FileSize = fld . RpfFile . FileSize ;
FileSizeText = TextUtil . GetBytesReadable ( FileSize ) ;
2018-01-11 09:10:03 +08:00
Attributes + = fld . RpfFile . Encryption . ToString ( ) + " encryption" ;
2017-09-21 18:33:05 +08:00
}
else
{
FileTypeText = "Folder" ;
ImageIndex = 1 ; //FOLDER imageIndex
var ic = fld . GetItemCount ( ) ;
FileSize = ic ;
FileSizeText = ic . ToString ( ) + " item" + ( ( ic ! = 1 ) ? "s" : "" ) ;
}
}
else
{
var fi = new FileInfo ( FullPath ) ;
if ( fi . Exists )
{
FileSize = fi . Length ;
FileSizeText = TextUtil . GetBytesReadable ( fi . Length ) ;
}
}
}
public int SortCompare ( MainListItem i , int col , SortOrder dir )
{
var desc = ( dir = = SortOrder . Descending ) ;
if ( Folder ! = null )
{
if ( i . Folder = = null ) return desc ? 1 : - 1 ;
}
else if ( i . Folder ! = null )
{
return desc ? - 1 : 1 ;
}
var i1 = this ;
var i2 = i ;
if ( desc )
{
i1 = i ;
i2 = this ;
}
switch ( col )
{
default :
case 0 : //Name column
return i1 . Name . CompareTo ( i2 . Name ) ;
case 1 : //Type column
var ftc = i1 . FileTypeText . CompareTo ( i2 . FileTypeText ) ;
if ( ftc = = 0 ) return i1 . Name . CompareTo ( i2 . Name ) ; //same type, sort by name...
return ftc ;
case 2 : //Size column
return i1 . FileSize . CompareTo ( i2 . FileSize ) ;
case 3 : //Attributes column
var ac = i1 . Attributes . CompareTo ( i2 . Attributes ) ;
if ( ac = = 0 ) return i1 . Name . CompareTo ( i2 . Name ) ; //same attributes, sort by name...
return ac ;
case 4 : //path column
return i1 . Path . CompareTo ( i2 . Path ) ;
}
//return i1.Name.CompareTo(i2.Name);
}
2018-01-10 11:17:30 +08:00
public RpfEntry GetRpfEntry ( )
{
RpfFile file = Folder ? . RpfFile ;
RpfDirectoryEntry fldr = Folder ? . RpfFolder ;
RpfEntry entry = File ;
if ( entry = = null )
{
if ( file ! = null )
{
//for an RPF file, get its entry in the parent (if any).
entry = file . ParentFileEntry ;
}
else if ( fldr ! = null )
{
//RPF folders are referenced in the item.Folder
entry = fldr ;
}
}
return entry ;
}
2017-09-21 18:33:05 +08:00
}
public class FileTypeInfo
{
public string Name { get ; set ; }
public string Extension { get ; set ; }
public int ImageIndex { get ; set ; }
public FileTypeAction DefaultAction { get ; set ; }
2017-12-14 18:49:41 +08:00
public List < FileTypeInfo > SubTypes { get ; set ; }
2017-09-21 18:33:05 +08:00
public FileTypeInfo ( string extension , string name , int imageindex , FileTypeAction defaultAction )
{
Name = name ;
Extension = extension ;
ImageIndex = imageindex ;
DefaultAction = defaultAction ;
}
2017-12-14 18:49:41 +08:00
public void AddSubType ( FileTypeInfo t )
{
if ( SubTypes = = null ) SubTypes = new List < FileTypeInfo > ( ) ;
SubTypes . Add ( t ) ;
}
2017-09-21 18:33:05 +08:00
}
public enum FileTypeAction
{
ViewHex = 0 ,
ViewText = 1 ,
ViewXml = 2 ,
ViewYtd = 3 ,
ViewYmt = 4 ,
ViewYmf = 5 ,
ViewYmap = 6 ,
ViewYtyp = 7 ,
ViewJPso = 8 ,
ViewModel = 9 ,
ViewCut = 10 ,
ViewAwc = 11 ,
ViewGxt = 12 ,
ViewRel = 13 ,
ViewFxc = 14 ,
ViewYwr = 15 ,
ViewYvr = 16 ,
2017-09-29 20:23:37 +08:00
ViewYcd = 17 ,
2017-12-14 18:49:41 +08:00
ViewCacheDat = 18 ,
2017-09-21 18:33:05 +08:00
}
2018-01-10 11:17:30 +08:00
2017-09-21 18:33:05 +08:00
}