RPF Explorer Edit mode

This commit is contained in:
dexyfex 2018-01-10 14:17:30 +11:00
parent 81daf0c23e
commit 65c549840f
6 changed files with 1750 additions and 122 deletions

View File

@ -59,6 +59,7 @@
this.ViewListMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ViewDetailsMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ToolsMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ToolsBinSearchMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ToolsRpfBrowserMenu = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator();
this.ToolsOptionsMenu = new System.Windows.Forms.ToolStripMenuItem();
@ -72,6 +73,8 @@
this.LocationTextBox = new CodeWalker.WinForms.ToolStripSpringTextBox();
this.GoButton = new System.Windows.Forms.ToolStripButton();
this.RefreshButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator10 = new System.Windows.Forms.ToolStripSeparator();
this.EditModeButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.SearchTextBox = new System.Windows.Forms.ToolStripTextBox();
this.SearchButton = new System.Windows.Forms.ToolStripSplitButton();
@ -97,6 +100,9 @@
this.ListContextExtractUncompressedMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractAllMenu = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
this.ListContextNewMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextNewFolderMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextNewRpfArchiveMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextImportXmlMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextImportRawMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextImportSeparator = new System.Windows.Forms.ToolStripSeparator();
@ -121,7 +127,6 @@
this.SaveFileDialog = new System.Windows.Forms.SaveFileDialog();
this.OpenFileDialog = new System.Windows.Forms.OpenFileDialog();
this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
this.ToolsBinSearchMenu = new System.Windows.Forms.ToolStripMenuItem();
this.MainMenu.SuspendLayout();
this.MainToolbar.SuspendLayout();
this.StatusBar.SuspendLayout();
@ -395,6 +400,13 @@
this.ToolsMenu.Size = new System.Drawing.Size(47, 20);
this.ToolsMenu.Text = "Tools";
//
// ToolsBinSearchMenu
//
this.ToolsBinSearchMenu.Name = "ToolsBinSearchMenu";
this.ToolsBinSearchMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsBinSearchMenu.Text = "Binary Search...";
this.ToolsBinSearchMenu.Click += new System.EventHandler(this.ToolsBinSearchMenu_Click);
//
// ToolsRpfBrowserMenu
//
this.ToolsRpfBrowserMenu.Name = "ToolsRpfBrowserMenu";
@ -424,6 +436,8 @@
this.LocationTextBox,
this.GoButton,
this.RefreshButton,
this.toolStripSeparator10,
this.EditModeButton,
this.toolStripSeparator1,
this.SearchTextBox,
this.SearchButton});
@ -496,7 +510,7 @@
// LocationTextBox
//
this.LocationTextBox.Name = "LocationTextBox";
this.LocationTextBox.Size = new System.Drawing.Size(510, 25);
this.LocationTextBox.Size = new System.Drawing.Size(423, 25);
this.LocationTextBox.Enter += new System.EventHandler(this.LocationTextBox_Enter);
this.LocationTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.LocationTextBox_KeyPress);
//
@ -522,6 +536,22 @@
this.RefreshButton.Text = "Refresh All";
this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click);
//
// toolStripSeparator10
//
this.toolStripSeparator10.Name = "toolStripSeparator10";
this.toolStripSeparator10.Size = new System.Drawing.Size(6, 25);
//
// EditModeButton
//
this.EditModeButton.Enabled = false;
this.EditModeButton.Image = ((System.Drawing.Image)(resources.GetObject("EditModeButton.Image")));
this.EditModeButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.EditModeButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.EditModeButton.Name = "EditModeButton";
this.EditModeButton.Size = new System.Drawing.Size(81, 22);
this.EditModeButton.Text = "Edit mode";
this.EditModeButton.Click += new System.EventHandler(this.EditModeButton_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
@ -676,6 +706,7 @@
this.MainListView.UseCompatibleStateImageBehavior = false;
this.MainListView.View = System.Windows.Forms.View.Details;
this.MainListView.VirtualMode = true;
this.MainListView.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.MainListView_AfterLabelEdit);
this.MainListView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.MainListView_ColumnClick);
this.MainListView.ItemActivate += new System.EventHandler(this.MainListView_ItemActivate);
this.MainListView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.MainListView_RetrieveVirtualItem);
@ -720,6 +751,7 @@
this.ListContextExtractUncompressedMenu,
this.ListContextExtractAllMenu,
this.toolStripSeparator5,
this.ListContextNewMenu,
this.ListContextImportXmlMenu,
this.ListContextImportRawMenu,
this.ListContextImportSeparator,
@ -735,7 +767,7 @@
this.ListContextEditSeparator,
this.ListContextSelectAllMenu});
this.ListContextMenu.Name = "MainContextMenu";
this.ListContextMenu.Size = new System.Drawing.Size(208, 392);
this.ListContextMenu.Size = new System.Drawing.Size(208, 414);
//
// ListContextViewMenu
//
@ -799,6 +831,29 @@
this.toolStripSeparator5.Name = "toolStripSeparator5";
this.toolStripSeparator5.Size = new System.Drawing.Size(204, 6);
//
// ListContextNewMenu
//
this.ListContextNewMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ListContextNewFolderMenu,
this.ListContextNewRpfArchiveMenu});
this.ListContextNewMenu.Name = "ListContextNewMenu";
this.ListContextNewMenu.Size = new System.Drawing.Size(207, 22);
this.ListContextNewMenu.Text = "New";
//
// ListContextNewFolderMenu
//
this.ListContextNewFolderMenu.Name = "ListContextNewFolderMenu";
this.ListContextNewFolderMenu.Size = new System.Drawing.Size(146, 22);
this.ListContextNewFolderMenu.Text = "Folder...";
this.ListContextNewFolderMenu.Click += new System.EventHandler(this.ListContextNewFolderMenu_Click);
//
// ListContextNewRpfArchiveMenu
//
this.ListContextNewRpfArchiveMenu.Name = "ListContextNewRpfArchiveMenu";
this.ListContextNewRpfArchiveMenu.Size = new System.Drawing.Size(146, 22);
this.ListContextNewRpfArchiveMenu.Text = "RPF Archive...";
this.ListContextNewRpfArchiveMenu.Click += new System.EventHandler(this.ListContextNewRpfArchiveMenu_Click);
//
// ListContextImportXmlMenu
//
this.ListContextImportXmlMenu.Image = ((System.Drawing.Image)(resources.GetObject("ListContextImportXmlMenu.Image")));
@ -958,12 +1013,9 @@
this.TreeContextCollapseAllMenu.Text = "Collapse All";
this.TreeContextCollapseAllMenu.Click += new System.EventHandler(this.TreeContextCollapseAllMenu_Click);
//
// ToolsBinSearchMenu
// OpenFileDialog
//
this.ToolsBinSearchMenu.Name = "ToolsBinSearchMenu";
this.ToolsBinSearchMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsBinSearchMenu.Text = "Binary Search...";
this.ToolsBinSearchMenu.Click += new System.EventHandler(this.ToolsBinSearchMenu_Click);
this.OpenFileDialog.Multiselect = true;
//
// ExploreForm
//
@ -1090,5 +1142,10 @@
private System.Windows.Forms.ToolStripSeparator ListContextOpenFileLocationSeparator;
private System.Windows.Forms.ToolStripMenuItem ListContextExtractUncompressedMenu;
private System.Windows.Forms.ToolStripMenuItem ToolsBinSearchMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextNewMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextNewFolderMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextNewRpfArchiveMenu;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator10;
private System.Windows.Forms.ToolStripButton EditModeButton;
}
}

View File

@ -418,6 +418,7 @@ namespace CodeWalker
GoButton.Enabled = true;
RefreshButton.Enabled = true;
SearchButton.Enabled = true;
EditModeButton.Enabled = true;
}
public void GoUp(MainTreeFolder toFolder = null)
@ -626,10 +627,7 @@ namespace CodeWalker
var exists = nodes.TryGetValue(parentpath, out node);
if (!exists)
{
node = new MainTreeFolder();
node.Name = parentname;
node.Path = parentpath;
node.FullPath = replpath + parentpath;
node = CreateRootDirTreeFolder(parentname, parentpath, replpath + parentpath);
nodes[parentpath] = node;
}
if (parentnode == null)
@ -656,12 +654,7 @@ namespace CodeWalker
rpf.ScanStructure(UpdateStatus, UpdateErrorLog);
node = new MainTreeFolder();
node.RpfFile = rpf;
node.RpfFolder = rpf.Root;
node.Name = rpf.Name;
node.Path = relpath;
node.FullPath = filepath;
node = CreateRpfTreeFolder(rpf, relpath, filepath);
RecurseMainTreeViewRPF(node, allRpfs);
@ -717,11 +710,7 @@ namespace CodeWalker
{
foreach (var dir in fld.Directories)
{
MainTreeFolder dtnf = new MainTreeFolder();
dtnf.RpfFolder = dir;
dtnf.Name = dir.Name;
dtnf.Path = dir.Path;
dtnf.FullPath = rootpath + dir.Path;
var dtnf = CreateRpfDirTreeFolder(dir, dir.Path, rootpath + dir.Path);
f.AddChild(dtnf);
RecurseMainTreeViewRPF(dtnf, allRpfs);
}
@ -737,35 +726,22 @@ namespace CodeWalker
{
foreach (var child in rpf.Children)
{
MainTreeFolder ctnf = new MainTreeFolder();
ctnf.RpfFile = child;
ctnf.RpfFolder = child.Root;
ctnf.Name = child.Name;
ctnf.Path = child.Path;
ctnf.FullPath = rootpath + child.Path;
var ctnf = CreateRpfTreeFolder(child, child.Path, rootpath + child.Path);
f.AddChildToHierarchy(ctnf);
RecurseMainTreeViewRPF(ctnf, allRpfs);
}
}
JenkIndex.Ensure(rpf.Name);
//JenkIndex.Ensure(rpf.Name);
if (rpf.AllEntries != null)
{
foreach (RpfEntry entry in rpf.AllEntries)
{
if (string.IsNullOrEmpty(entry.NameLower)) continue;
JenkIndex.Ensure(entry.Name);
JenkIndex.Ensure(entry.NameLower);
var shortnamel = entry.GetShortNameLower();
JenkIndex.Ensure(shortnamel);
entry.ShortNameHash = JenkHash.GenHash(shortnamel);
entry.NameHash = JenkHash.GenHash(entry.NameLower);
int ind = entry.Name.LastIndexOf('.');
if (ind > 0)
{
var shortname = entry.Name.Substring(0, ind);
var shortnamel = entry.NameLower.Substring(0, ind);
JenkIndex.Ensure(shortname);
JenkIndex.Ensure(shortnamel);
entry.ShortNameHash = JenkHash.GenHash(shortnamel);
}
}
}
}
@ -786,6 +762,7 @@ namespace CodeWalker
ForwardButton.Enabled = false;
RefreshButton.Enabled = false;
SearchButton.Enabled = false;
EditModeButton.Enabled = false;
MainTreeView.Nodes.Clear();
MainListView.VirtualListSize = 0; //also clear the list view...
}
@ -841,6 +818,8 @@ namespace CodeWalker
tn.ToolTipText = f.Path;
tn.Tag = f;
f.TreeNode = tn;
if (f.Children != null)
{
f.Children.Sort((n1, n2) => n1.Name.CompareTo(n2.Name));
@ -882,6 +861,112 @@ namespace CodeWalker
}
catch { }
}
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;
}
private void RefreshMainListView()
{
@ -1430,12 +1515,11 @@ namespace CodeWalker
bool isfile = false;
bool isfolder = false;
bool isfilesys = false;
bool issearch = CurrentFolder?.IsSearchResults ?? false;
bool canview = false;
bool canexportxml = false;
bool editmode = false;//todo: set this for edit mode
bool canimport = editmode;
bool canimport = EditMode && (CurrentFolder?.RpfFolder != null) && !issearch;
bool canedit = false;
bool issearch = CurrentFolder?.IsSearchResults ?? false;
if (item != null)
{
@ -1445,7 +1529,7 @@ namespace CodeWalker
canexportxml = CanExportXml(item);
isitem = true;
isfile = !isfolder;
canedit = editmode && !canimport;
canedit = EditMode && !issearch;
}
@ -1456,9 +1540,10 @@ namespace CodeWalker
ListContextExtractRawMenu.Enabled = isfile;
ListContextExtractUncompressedMenu.Enabled = isfile;
ListContextNewMenu.Visible = EditMode;
ListContextImportRawMenu.Visible = canimport;
ListContextImportXmlMenu.Visible = canimport;
ListContextImportSeparator.Visible = canimport;
ListContextImportSeparator.Visible = EditMode;
ListContextCopyMenu.Enabled = isfile;
ListContextCopyPathMenu.Enabled = isitem;
@ -1477,6 +1562,48 @@ namespace CodeWalker
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;
}
private bool IsFilenameOk(string name)
{
foreach (var ic in Path.GetInvalidFileNameChars())
{
if (name.Contains(ic))
{
return false;
}
}
return true;
}
private void ViewSelected()
{
for (int i = 0; i < MainListView.SelectedIndices.Count; i++)
@ -1798,15 +1925,182 @@ namespace CodeWalker
MessageBox.Show("Errors were encountered:\n" + errstr);
}
}
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;
var rootpath = GetRootPath();
string fullpath = rootpath + relpath;
RpfDirectoryEntry newdir = null;
MainTreeFolder node = null;
try
{
if (CurrentFolder.RpfFolder != null)
{
//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)
{
//adding a new RPF as a child of another
newrpf = RpfFile.CreateNew(CurrentFolder.RpfFolder, fname, encryption);
}
else
{
//adding a new RPF in the filesystem
newrpf = RpfFile.CreateNew(Settings.Default.GTAFolder, relpath, encryption);
}
}
catch (Exception ex)
{
MessageBox.Show("Error creating archive: " + ex.Message, "Unable to create new archive");
return;
}
if (newrpf != null)
{
var node = CreateRpfTreeFolder(newrpf, newrpf.Path, GetRootPath() + newrpf.Path);
RecurseMainTreeViewRPF(node, AllRpfs);
AddNewFolderTreeNode(node);
}
}
private void ImportXml()
{
if (!EditMode) return;
MessageBox.Show("Edit mode functions not yet implemented...");
if (CurrentFolder?.IsSearchResults ?? false) return;
MessageBox.Show("Import XML TODO...");
}
private void ImportRaw()
{
if (!EditMode) return;
MessageBox.Show("Edit mode functions not yet implemented...");
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;
}
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;
}
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;
}
}
byte[] data = File.ReadAllBytes(fpath);
RpfFile.CreateFile(parentrpffldr, fname, data);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Unable to import file");
return;
}
CurrentFolder.ListItems = null;
RefreshMainListView();
}
private void CopySelected()
{
@ -1863,19 +2157,156 @@ namespace CodeWalker
private void RenameSelected()
{
if (!EditMode) return;
if (MainListView.SelectedItems.Count != 1) return;
MessageBox.Show("Edit mode functions not yet implemented...");
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)
{
//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;
}
}
private void ReplaceSelected()
{
if (!EditMode) return;
if (MainListView.SelectedItems.Count != 1) return;
MessageBox.Show("Edit mode functions not yet implemented...");
if (CurrentFolder?.IsSearchResults ?? false) return;
if (MainListView.SelectedIndices.Count != 1) return;
MessageBox.Show("ReplaceSelected TODO...");
//delete the selected items, and replace with... choose
}
private void DeleteSelected()
{
if (!EditMode) return;
MessageBox.Show("Edit mode functions not yet implemented...");
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
{
var parent = item.Parent;
if (parent.RpfFolder != null)
{
//delete an item in an RPF.
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;
}
}
private void SelectAll()
{
@ -2116,6 +2547,14 @@ namespace CodeWalker
}
}
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);
}
}
private void BackButton_ButtonClick(object sender, EventArgs e)
{
GoBack();
@ -2183,6 +2622,11 @@ namespace CodeWalker
});
}
private void EditModeButton_Click(object sender, EventArgs e)
{
EnableEditMode(!EditMode);
}
private void SearchTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 13)
@ -2298,6 +2742,16 @@ namespace CodeWalker
ExtractAll();
}
private void ListContextNewFolderMenu_Click(object sender, EventArgs e)
{
NewFolder();
}
private void ListContextNewRpfArchiveMenu_Click(object sender, EventArgs e)
{
NewRpfArchive();
}
private void ListContextImportXmlMenu_Click(object sender, EventArgs e)
{
ImportXml();
@ -2474,6 +2928,7 @@ namespace CodeWalker
public MainTreeFolder Parent { get; set; }
public List<MainTreeFolder> Children { get; set; }
public List<MainListItem> ListItems { get; set; }
public TreeNode TreeNode { get; set; }
public bool IsSearchResults { get; set; }
public string SearchTerm { get; set; }
@ -2768,6 +3223,27 @@ namespace CodeWalker
//return i1.Name.CompareTo(i2.Name);
}
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;
}
}
@ -2817,4 +3293,41 @@ namespace CodeWalker
ViewCacheDat = 18,
}
public static class Prompt
{
public static string ShowDialog(IWin32Window owner, string text, string caption, string defaultvalue = "")
{
Form prompt = new Form()
{
Width = 450,
Height = 150,
FormBorderStyle = FormBorderStyle.FixedDialog,
Text = caption,
StartPosition = FormStartPosition.CenterParent,
MaximizeBox = false,
MinimizeBox = false
};
Label textLabel = new Label() { Left = 30, Top = 20, Width = 370, Height = 20, Text = text, };
TextBox textBox = new TextBox() { Left = 30, Top = 40, Width = 370, Text = defaultvalue };
Button cancel = new Button() { Text = "Cancel", Left = 230, Width = 80, Top = 70, DialogResult = DialogResult.Cancel };
Button confirmation = new Button() { Text = "Ok", Left = 320, Width = 80, Top = 70, DialogResult = DialogResult.OK };
cancel.Click += (sender, e) => { prompt.Close(); };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(textBox);
prompt.Controls.Add(confirmation);
prompt.Controls.Add(cancel);
prompt.Controls.Add(textLabel);
prompt.AcceptButton = confirmation;
return prompt.ShowDialog(owner) == DialogResult.OK ? textBox.Text : "";
}
}
}

View File

@ -258,6 +258,19 @@
GvZP7/3NqMHOyEACQlVVHxGjBiOpbdu1KIokGRDjZCqQj/3WzsPozi3L8oNTCXEyAqDv+93PykyM7hyb
OEgy37QlAVUSCxs9tKCADeqLDGjzT4Gu676IErtUgaDbuTwDCUhYccAhyS2wicsjWZXIJ9R1vYsbPdjm
ePJI4uQjAWAz8UYNxvNEhJNVrq7ziMzzN2pq/DDMhNNPcE6+bs69AUIIBqGH+5QgAAAAAElFTkSuQmCC
</value>
</data>
<data name="EditModeButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGKSURBVDhPjZM7T8JQFMfP4GpiGFhcGBwddPBVkBK0kKhR
J8UoD4OoRGTxMRs3EyYHE2N8EF0wJiwuBhUTwecAH8DvwODe4z3tQSi3GP/JSZt7z+93e9tbsEswGPRp
mlYUV6Sie1F9PN0+otkVCATOCRqYXUdnLIvlEx/uJMYNEc+5uL0RXq1CTd6pEHZHjxFSHwgb76g/eUS5
8e5QxcicZoiolxjGjZWxZ/EAHSvXCJsCZBiSJHCjXlTMehzB6oUHL/dVQ8S4KYD0pwRD8s0C6w/DZt0P
2QhsYFhjQQusFwZlgSOek2BYfbWFq2eKLHBGsxIMCSFoganKR25JkM9sj0kvjECIvyAslxFioqIlhEgJ
e2d2SZBn3BDEyPiVG5X23Ap3LtwYqxPDuBkxUEmGNWnPzTCEn1GZjBBcYawROqpiopbZ8v/CtN9mmB+9
1vZY1yV7KT9+37JAwB1LBeyfTv8N11OX0LGtniroCF2hd2L+f3A9qqp2iWbL30hjPP3/CJi+jvVtWwLw
A4Rmgl76+inbAAAAAElFTkSuQmCC
</value>
</data>
<data name="SearchGlobalButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
@ -300,7 +313,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADo
HwAAAk1TRnQBSQFMAgEBGAEAAaABAAGgAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
HwAAAk1TRnQBSQFMAgEBGAEAAdABAAHQAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAAXADAAEBAQABCAYAARwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA

View File

@ -21,7 +21,7 @@ namespace CodeWalker.GameFiles
{
RpfFileEntry = entry;
Type = type;
MemoryUsage = (entry != null) ? entry.FileSize : 0;
MemoryUsage = (entry != null) ? entry.GetFileSize() : 0;
if (entry is RpfResourceFileEntry)
{
var resent = entry as RpfResourceFileEntry;

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,10 @@ namespace CodeWalker.GameFiles
{
return DecryptAESData(data, GTA5Keys.PC_AES_KEY);
}
public static byte[] EncryptAES(byte[] data)
{
return EncryptAESData(data, GTA5Keys.PC_AES_KEY);
}
public static byte[] DecryptAESData(byte[] data, byte[] key, int rounds = 1)
{
@ -247,5 +251,179 @@ namespace CodeWalker.GameFiles
public static byte[] EncryptNG(byte[] data, string name, uint length)
{
byte[] key = GetNGKey(name, length);
return EncryptNG(data, key);
}
public static byte[] EncryptNG(byte[] data, byte[] key)
{
if ((GTA5Keys.PC_NG_ENCRYPT_TABLES == null) || (GTA5Keys.PC_NG_ENCRYPT_LUTs == null))
{
throw new Exception("Unable to encrypt - tables not loaded.");
}
var encryptedData = new byte[data.Length];
var keyuints = new uint[key.Length / 4];
Buffer.BlockCopy(key, 0, keyuints, 0, key.Length);
for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
{
byte[] decryptedBlock = new byte[16];
Array.Copy(data, 16 * blockIndex, decryptedBlock, 0, 16);
byte[] encryptedBlock = EncryptBlock(decryptedBlock, keyuints);
Array.Copy(encryptedBlock, 0, encryptedData, 16 * blockIndex, 16);
}
if (data.Length % 16 != 0)
{
var left = data.Length % 16;
Buffer.BlockCopy(data, data.Length - left, encryptedData, data.Length - left, left);
}
return encryptedData;
}
public static byte[] EncryptBlock(byte[] data, uint[] key)
{
var buffer = data;
// prepare key...
var subKeys = new uint[17][];
for (int i = 0; i < 17; i++)
{
subKeys[i] = new uint[4];
subKeys[i][0] = key[4 * i + 0];
subKeys[i][1] = key[4 * i + 1];
subKeys[i][2] = key[4 * i + 2];
subKeys[i][3] = key[4 * i + 3];
}
buffer = EncryptRoundA(buffer, subKeys[16], GTA5Keys.PC_NG_ENCRYPT_TABLES[16]);
for (int k = 15; k >= 2; k--)
buffer = EncryptRoundB_LUT(buffer, subKeys[k], GTA5Keys.PC_NG_ENCRYPT_LUTs[k]);
buffer = EncryptRoundA(buffer, subKeys[1], GTA5Keys.PC_NG_ENCRYPT_TABLES[1]);
buffer = EncryptRoundA(buffer, subKeys[0], GTA5Keys.PC_NG_ENCRYPT_TABLES[0]);
return buffer;
}
public static byte[] EncryptRoundA(byte[] data, uint[] key, uint[][] table)
{
// apply xor to data first...
var xorbuf = new byte[16];
Buffer.BlockCopy(key, 0, xorbuf, 0, 16);
var x1 =
table[0][data[0] ^ xorbuf[0]] ^
table[1][data[1] ^ xorbuf[1]] ^
table[2][data[2] ^ xorbuf[2]] ^
table[3][data[3] ^ xorbuf[3]];
var x2 =
table[4][data[4] ^ xorbuf[4]] ^
table[5][data[5] ^ xorbuf[5]] ^
table[6][data[6] ^ xorbuf[6]] ^
table[7][data[7] ^ xorbuf[7]];
var x3 =
table[8][data[8] ^ xorbuf[8]] ^
table[9][data[9] ^ xorbuf[9]] ^
table[10][data[10] ^ xorbuf[10]] ^
table[11][data[11] ^ xorbuf[11]];
var x4 =
table[12][data[12] ^ xorbuf[12]] ^
table[13][data[13] ^ xorbuf[13]] ^
table[14][data[14] ^ xorbuf[14]] ^
table[15][data[15] ^ xorbuf[15]];
var buf = new byte[16];
Array.Copy(BitConverter.GetBytes(x1), 0, buf, 0, 4);
Array.Copy(BitConverter.GetBytes(x2), 0, buf, 4, 4);
Array.Copy(BitConverter.GetBytes(x3), 0, buf, 8, 4);
Array.Copy(BitConverter.GetBytes(x4), 0, buf, 12, 4);
return buf;
}
public static byte[] EncryptRoundA_LUT(byte[] dataOld, uint[] key, GTA5NGLUT[] lut)
{
var data = (byte[])dataOld.Clone();
// apply xor to data first...
var xorbuf = new byte[16];
Buffer.BlockCopy(key, 0, xorbuf, 0, 16);
for (int y = 0; y < 16; y++)
{
data[y] ^= xorbuf[y];
}
return new byte[] {
lut[0].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[1].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[2].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[3].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[4].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[5].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[6].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[7].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[8].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[9].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[10].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[11].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[12].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[13].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[14].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[15].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0))
};
}
public static byte[] EncryptRoundB_LUT(byte[] dataOld, uint[] key, GTA5NGLUT[] lut)
{
var data = (byte[])dataOld.Clone();
// apply xor to data first...
var xorbuf = new byte[16];
Buffer.BlockCopy(key, 0, xorbuf, 0, 16);
for (int y = 0; y < 16; y++)
{
data[y] ^= xorbuf[y];
}
return new byte[] {
lut[0].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[1].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[2].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[3].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[4].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[5].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[6].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[7].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[8].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0)),
lut[9].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[10].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[11].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[12].LookUp(BitConverter.ToUInt32( new byte[] { data[12], data[13], data[14], data[15] }, 0)),
lut[13].LookUp(BitConverter.ToUInt32( new byte[] { data[0], data[1], data[2], data[3] }, 0)),
lut[14].LookUp(BitConverter.ToUInt32( new byte[] { data[4], data[5], data[6], data[7] }, 0)),
lut[15].LookUp(BitConverter.ToUInt32( new byte[] { data[8], data[9], data[10], data[11] }, 0))};
}
}
}