using CodeWalker.GameFiles; using CodeWalker.Properties; using CodeWalker.Utils; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace CodeWalker { public partial class BrowseForm : Form { private volatile bool KeysLoaded = false; private volatile bool InProgress = false; private volatile bool AbortOperation = false; private int TotalFileCount = 0; private List ScannedFiles = new List(); private List RootFiles = new List(); private List SearchResults = new List(); private RpfEntry SelectedEntry = null; private int SelectedOffset = -1; private int SelectedLength = 0; private bool TextureTabPageVisible = false; private bool FlatStructure = false; public BrowseForm() { InitializeComponent(); } private void BrowseForm_Load(object sender, EventArgs e) { var info = DetailsPropertyGrid.GetType().GetProperty("Controls"); var collection = info.GetValue(DetailsPropertyGrid, null) as Control.ControlCollection; foreach (var control in collection) { var ctyp = control.GetType(); if (ctyp.Name == "PropertyGridView") { var prop = ctyp.GetField("labelRatio"); var val = prop.GetValue(control); prop.SetValue(control, 4.0); //somehow this sets the width of the property grid's label column... } } FolderTextBox.Text = GTAFolder.CurrentGTAFolder; DataHexLineCombo.Text = "16"; DataTextBox.SetTabStopWidth(3); HideTexturesTab(); try { GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key); KeysLoaded = true; UpdateStatus("Ready to scan..."); } catch { UpdateStatus("Keys not loaded! This should not happen."); } } private void BrowseForm_FormClosed(object sender, FormClosedEventArgs e) { if (!TextureTabPageVisible) { TexturesTabPage.Dispose(); } } private void FolderBrowseButton_Click(object sender, EventArgs e) { GTAFolder.UpdateGTAFolder(false); FolderTextBox.Text = GTAFolder.CurrentGTAFolder; } private void ScanButton_Click(object sender, EventArgs e) { if (InProgress) return; if (!KeysLoaded) //this shouldn't ever really happen anymore { MessageBox.Show("Please scan a GTA 5 exe dump for keys first, or include key files in this app's folder!"); return; } if (!Directory.Exists(FolderTextBox.Text)) { MessageBox.Show("Folder doesn't exist: " + FolderTextBox.Text); return; } InProgress = true; AbortOperation = false; ScannedFiles.Clear(); RootFiles.Clear(); MainTreeView.Nodes.Clear(); string searchpath = FolderTextBox.Text; string replpath = searchpath + "\\"; Task.Run(() => { UpdateStatus("Starting scan..."); string[] allfiles = Directory.GetFiles(searchpath, "*.rpf", SearchOption.AllDirectories); uint totrpfs = 0; uint totfiles = 0; uint totfolders = 0; uint totresfiles = 0; uint totbinfiles = 0; foreach (string rpfpath in allfiles) { if (AbortOperation) { UpdateStatus("Scan aborted!"); InProgress = false; return; } RpfFile rf = new RpfFile(rpfpath, rpfpath.Replace(replpath, "")); UpdateStatus("Scanning " + rf.Name + "..."); rf.ScanStructure(UpdateStatus, UpdateStatus); totrpfs += rf.GrandTotalRpfCount; totfiles += rf.GrandTotalFileCount; totfolders += rf.GrandTotalFolderCount; totresfiles += rf.GrandTotalResourceCount; totbinfiles += rf.GrandTotalBinaryFileCount; AddScannedFile(rf, null, true); RootFiles.Add(rf); } UpdateStatus(string.Format("Scan complete. {0} RPF files, {1} total files, {2} total folders, {3} resources, {4} binary files.", totrpfs, totfiles, totfolders, totresfiles, totbinfiles)); InProgress = false; TotalFileCount = (int)totfiles; }); } private void UpdateStatus(string text) { try { if (InvokeRequired) { Invoke(new Action(() => { UpdateStatus(text); })); } else { StatusLabel.Text = text; } } catch { } } private void ClearFiles() { MainTreeView.Nodes.Clear(); } private void AddScannedFile(RpfFile file, TreeNode node, bool addToList = false) { try { if (InvokeRequired) { Invoke(new Action(() => { AddScannedFile(file, node, addToList); })); } else { var cnode = AddFileNode(file, node); if (FlatStructure) cnode = null; foreach (RpfFile cfile in file.Children) { AddScannedFile(cfile, cnode, addToList); } if (addToList) { ScannedFiles.Add(file); } } } catch { } } private TreeNode AddFileNode(RpfFile file, TreeNode n) { var nodes = (n == null) ? MainTreeView.Nodes : n.Nodes; TreeNode node = nodes.Add(file.Path); node.Tag = file; foreach (RpfEntry entry in file.AllEntries) { if (entry is RpfFileEntry) { bool show = !entry.NameLower.EndsWith(".rpf"); //rpf entries get their own root node.. if (show) { //string text = entry.Path.Substring(file.Path.Length + 1); //includes \ on the end //TreeNode cnode = node.Nodes.Add(text); //cnode.Tag = entry; TreeNode cnode = AddEntryNode(entry, node); } } } //make sure it's all in jenkindex... JenkIndex.Ensure(file.Name); foreach (RpfEntry entry in file.AllEntries) { if (string.IsNullOrEmpty(entry.Name)) continue; JenkIndex.Ensure(entry.Name); JenkIndex.Ensure(entry.NameLower); int ind = entry.Name.LastIndexOf('.'); if (ind > 0) { JenkIndex.Ensure(entry.Name.Substring(0, ind)); JenkIndex.Ensure(entry.NameLower.Substring(0, ind)); } } return node; //TreeNode lastNode = null; //string subPathAgg; //subPathAgg = string.Empty; //foreach (string subPath in file.Path.Split('\\')) //{ // subPathAgg += subPath + '\\'; // TreeNode[] nodes = MainTreeView.Nodes.Find(subPathAgg, true); // if (nodes.Length == 0) // { // if (lastNode == null) // { // lastNode = MainTreeView.Nodes.Add(subPathAgg, subPath); // } // else // { // lastNode = lastNode.Nodes.Add(subPathAgg, subPath); // } // } // else // { // lastNode = nodes[0]; // } //} //lastNode.Tag = file; } private TreeNode AddEntryNode(RpfEntry entry, TreeNode node) { string text = entry.Path.Substring(entry.File.Path.Length + 1); //includes \ on the end TreeNode cnode = (node != null) ? node.Nodes.Add(text) : MainTreeView.Nodes.Add(text); cnode.Tag = entry; return cnode; } private void MainTreeView_AfterSelect(object sender, TreeViewEventArgs e) { if (e.Node == null) return; if (!e.Node.IsSelected) return; //only do this for selected node if (MainTreeView.SelectedNode == null) return; SelectedEntry = MainTreeView.SelectedNode.Tag as RpfEntry; SelectedOffset = -1; SelectedLength = 0; SelectFile(); } private void DataTextRadio_CheckedChanged(object sender, EventArgs e) { SelectFile(); } private void DataHexLineCombo_SelectedIndexChanged(object sender, EventArgs e) { SelectFile(); } private void DataHexRadio_CheckedChanged(object sender, EventArgs e) { SelectFile(); } private void SelectFile() { SelectFile(SelectedEntry, SelectedOffset, SelectedLength); } private void SelectFile(RpfEntry entry, int offset, int length) { SelectedEntry = entry; SelectedOffset = offset; SelectedLength = length; RpfFileEntry rfe = entry as RpfFileEntry; if (rfe == null) { RpfDirectoryEntry rde = entry as RpfDirectoryEntry; if (rde != null) { FileInfoLabel.Text = rde.Path + " (Directory)"; DataTextBox.Text = "[Please select a data file]"; } else { FileInfoLabel.Text = "[Nothing selected]"; DataTextBox.Text = "[Please select a data file]"; } ShowTextures(null); return; } Cursor = Cursors.WaitCursor; string typestr = "Resource"; if (rfe is RpfBinaryFileEntry) { typestr = "Binary"; } byte[] data = rfe.File.ExtractFile(rfe); int datalen = (data != null) ? data.Length : 0; FileInfoLabel.Text = rfe.Path + " (" + typestr + " file) - " + TextUtil.GetBytesReadable(datalen); if (ShowLargeFileContentsCheckBox.Checked || (datalen < 524287)) //512K { DisplayFileContentsText(rfe, data, length, offset); } else { DataTextBox.Text = "[Filesize >512KB. Select the Show large files option to view its contents]"; } bool istexdict = false; if (rfe.NameLower.EndsWith(".ymap")) { YmapFile ymap = new YmapFile(rfe); ymap.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ymap; } else if (rfe.NameLower.EndsWith(".ytyp")) { YtypFile ytyp = new YtypFile(); ytyp.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ytyp; } else if (rfe.NameLower.EndsWith(".ymf")) { YmfFile ymf = new YmfFile(); ymf.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ymf; } else if (rfe.NameLower.EndsWith(".ymt")) { YmtFile ymt = new YmtFile(); ymt.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ymt; } else if (rfe.NameLower.EndsWith(".ybn")) { YbnFile ybn = new YbnFile(); ybn.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ybn; } else if (rfe.NameLower.EndsWith(".fxc")) { FxcFile fxc = new FxcFile(); fxc.Load(data, rfe); DetailsPropertyGrid.SelectedObject = fxc; } else if (rfe.NameLower.EndsWith(".yft")) { YftFile yft = new YftFile(); yft.Load(data, rfe); DetailsPropertyGrid.SelectedObject = yft; if ((yft.Fragment != null) && (yft.Fragment.Drawable != null) && (yft.Fragment.Drawable.ShaderGroup != null) && (yft.Fragment.Drawable.ShaderGroup.TextureDictionary != null)) { ShowTextures(yft.Fragment.Drawable.ShaderGroup.TextureDictionary); istexdict = true; } } else if (rfe.NameLower.EndsWith(".ydr")) { YdrFile ydr = new YdrFile(); ydr.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ydr; if ((ydr.Drawable != null) && (ydr.Drawable.ShaderGroup != null) && (ydr.Drawable.ShaderGroup.TextureDictionary != null)) { ShowTextures(ydr.Drawable.ShaderGroup.TextureDictionary); istexdict = true; } } else if (rfe.NameLower.EndsWith(".ydd")) { YddFile ydd = new YddFile(); ydd.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ydd; //todo: show embedded texdicts in ydd's? is this possible? } else if (rfe.NameLower.EndsWith(".ytd")) { YtdFile ytd = new YtdFile(); ytd.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ytd; ShowTextures(ytd.TextureDict); istexdict = true; } else if (rfe.NameLower.EndsWith(".ycd")) { YcdFile ycd = new YcdFile(); ycd.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ycd; } else if (rfe.NameLower.EndsWith(".ynd")) { YndFile ynd = new YndFile(); ynd.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ynd; } else if (rfe.NameLower.EndsWith(".ynv")) { YnvFile ynv = new YnvFile(); ynv.Load(data, rfe); DetailsPropertyGrid.SelectedObject = ynv; } else if (rfe.NameLower.EndsWith("_cache_y.dat")) { CacheDatFile cdf = new CacheDatFile(); cdf.Load(data, rfe); DetailsPropertyGrid.SelectedObject = cdf; } else if (rfe.NameLower.EndsWith(".rel")) { RelFile rel = new RelFile(rfe); rel.Load(data, rfe); DetailsPropertyGrid.SelectedObject = rel; } else if (rfe.NameLower.EndsWith(".gxt2")) { Gxt2File gxt2 = new Gxt2File(); gxt2.Load(data, rfe); DetailsPropertyGrid.SelectedObject = gxt2; } else if (rfe.NameLower.EndsWith(".pso")) { JPsoFile pso = new JPsoFile(); pso.Load(data, rfe); DetailsPropertyGrid.SelectedObject = pso; } else { DetailsPropertyGrid.SelectedObject = null; } if (!istexdict) { ShowTextures(null); } Cursor = Cursors.Default; } private void DisplayFileContentsText(RpfFileEntry rfe, byte[] data, int length, int offset) { if (data == null) { Cursor = Cursors.Default; DataTextBox.Text = "[Error extracting file! " + rfe.File.LastError + "]"; return; } int selline = -1; int selstartc = -1; int selendc = -1; if (DataHexRadio.Checked) { int charsperln = int.Parse(DataHexLineCombo.Text); int lines = (data.Length / charsperln) + (((data.Length % charsperln) > 0) ? 1 : 0); StringBuilder hexb = new StringBuilder(); StringBuilder texb = new StringBuilder(); StringBuilder finb = new StringBuilder(); if (offset > 0) { selline = offset / charsperln; } for (int i = 0; i < lines; i++) { int pos = i * charsperln; int poslim = pos + charsperln; hexb.Clear(); texb.Clear(); hexb.AppendFormat("{0:X4}: ", pos); for (int c = pos; c < poslim; c++) { if (c < data.Length) { byte b = data[c]; hexb.AppendFormat("{0:X2} ", b); if (char.IsControl((char)b)) { texb.Append("."); } else { texb.Append(Encoding.ASCII.GetString(data, c, 1)); } } else { hexb.Append(" "); texb.Append(" "); } } if (i == selline) selstartc = finb.Length; finb.AppendLine(hexb.ToString() + "| " + texb.ToString()); if (i == selline) selendc = finb.Length - 1; } DataTextBox.Text = finb.ToString(); } else { string text = Encoding.UTF8.GetString(data); DataTextBox.Text = text; if (offset > 0) { selstartc = offset; selendc = offset + length; } } if ((selstartc > 0) && (selendc > 0)) { DataTextBox.SelectionStart = selstartc; DataTextBox.SelectionLength = selendc - selstartc; DataTextBox.ScrollToCaret(); } } private void ShowTextures(TextureDictionary td) { SelTexturesListView.Items.Clear(); SelTexturePictureBox.Image = null; SelTextureNameTextBox.Text = string.Empty; SelTextureDimensionsLabel.Text = "-"; SelTextureMipLabel.Text = "0"; SelTextureMipTrackBar.Value = 0; SelTextureMipTrackBar.Maximum = 0; if (td == null) { HideTexturesTab(); return; } if (!SelectionTabControl.TabPages.Contains(TexturesTabPage)) { SelectionTabControl.TabPages.Add(TexturesTabPage); } if ((td.Textures == null) || (td.Textures.data_items == null)) return; var texs = td.Textures.data_items; for (int i = 0; i < texs.Length; i++) { var tex = texs[i]; ListViewItem lvi = SelTexturesListView.Items.Add(tex.Name); lvi.Tag = tex; } } private void HideTexturesTab() { if (SelectionTabControl.TabPages.Contains(TexturesTabPage)) { SelectionTabControl.TabPages.Remove(TexturesTabPage); } } private void ShowTextureMip(Texture tex, int mip, bool mipchange) { if (tex == null) { SelTexturePictureBox.Image = null; SelTextureNameTextBox.Text = string.Empty; SelTextureDimensionsLabel.Text = "-"; SelTextureMipLabel.Text = "0"; SelTextureMipTrackBar.Value = 0; SelTextureMipTrackBar.Maximum = 0; return; } if (mipchange) { if (mip >= tex.Levels) mip = tex.Levels - 1; } else { SelTextureMipTrackBar.Maximum = tex.Levels - 1; } SelTextureNameTextBox.Text = tex.Name; try { int cmip = Math.Min(Math.Max(mip, 0), tex.Levels - 1); byte[] pixels = DDSIO.GetPixels(tex, cmip); int w = tex.Width >> cmip; int h = tex.Height >> cmip; Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb); if (pixels != null) { var BoundsRect = new System.Drawing.Rectangle(0, 0, w, h); BitmapData bmpData = bmp.LockBits(BoundsRect, ImageLockMode.WriteOnly, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int bytes = bmpData.Stride * bmp.Height; Marshal.Copy(pixels, 0, ptr, bytes); bmp.UnlockBits(bmpData); } SelTexturePictureBox.Image = bmp; SelTextureDimensionsLabel.Text = w.ToString() + " x " + h.ToString(); } catch (Exception ex) { MessageBox.Show("Error reading texture mip:\n" + ex.ToString()); SelTexturePictureBox.Image = null; } } private void AbortButton_Click(object sender, EventArgs e) { AbortOperation = true; } private void TestAllButton_Click(object sender, EventArgs e) { if (InProgress) return; if (ScannedFiles.Count == 0) { MessageBox.Show("Please scan the GTAV folder first."); return; } AbortOperation = false; InProgress = true; DataTextBox.Text = string.Empty; FileInfoLabel.Text = "[File test results]"; Task.Run(() => { UpdateStatus("Starting test..."); StringBuilder sbout = new StringBuilder(); int errcount = 0; int curfile = 1; int totrpfs = ScannedFiles.Count; long totbytes = 0; foreach (RpfFile file in ScannedFiles) { if (AbortOperation) { UpdateStatus("Test aborted."); InProgress = false; return; } UpdateStatus(curfile.ToString() + "/" + totrpfs.ToString() + ": Testing " + file.FilePath + "..."); string errorstr = file.TestExtractAllFiles(); if (!string.IsNullOrEmpty(errorstr)) { AddTestError(errorstr); sbout.Append(errorstr); errcount++; } totbytes += file.ExtractedByteCount; curfile++; } UpdateStatus("Test complete. " + errcount.ToString() + " problems encountered, " + totbytes.ToString() + " total bytes extracted."); InProgress = false; }); } private void AddTestError(string error) { try { if (InvokeRequired) { Invoke(new Action(() => { AddTestError(error); })); } else { DataTextBox.AppendText(error); } } catch { } } private void Find() { if (InProgress) return; if (ScannedFiles.Count == 0) { MessageBox.Show("Please scan the GTAV folder first."); return; } string find = FindTextBox.Text.ToLowerInvariant(); Cursor = Cursors.WaitCursor; if (string.IsNullOrEmpty(find)) { ClearFiles(); foreach (RpfFile file in RootFiles) { AddScannedFile(file, null); //reset the file tree... slow :( } } else { ClearFiles(); int count = 0; int max = 500; foreach (RpfFile file in ScannedFiles) { if (file.Name.ToLowerInvariant().Contains(find)) { AddFileNode(file, null); count++; } foreach (RpfEntry entry in file.AllEntries) { if (entry.NameLower.Contains(find)) { if (entry is RpfDirectoryEntry) { RpfDirectoryEntry direntry = entry as RpfDirectoryEntry; TreeNode node = AddEntryNode(entry, null); foreach (RpfFileEntry cfentry in direntry.Files) { //if (cfentry.Name.EndsWith(".rpf", StringComparison.InvariantCultureIgnoreCase)) continue; AddEntryNode(cfentry, node); } count++; } else if (entry is RpfBinaryFileEntry) { if (entry.NameLower.EndsWith(".rpf", StringComparison.InvariantCultureIgnoreCase)) continue; AddEntryNode(entry, null); count++; } else if (entry is RpfResourceFileEntry) { AddEntryNode(entry, null); count++; } } if (count >= max) { MessageBox.Show("Search results limited to " + max.ToString() + " entries."); break; } } if (count >= max) { break; } } } Cursor = Cursors.Default; } private void FindTextBox_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == 13) { Find(); e.Handled = true; } } private void FindButton_Click(object sender, EventArgs e) { Find(); } private void ExportButton_Click(object sender, EventArgs e) { if (InProgress) return; if (ScannedFiles.Count == 0) { MessageBox.Show("Please scan the GTAV folder first."); return; } TreeNode node = MainTreeView.SelectedNode; if (node == null) { MessageBox.Show("Please select a file to export."); return; } RpfFileEntry rfe = node.Tag as RpfFileEntry; if (rfe == null) { MessageBox.Show("Please select a file to export."); return; } SaveFileDialog.FileName = rfe.Name; if (SaveFileDialog.ShowDialog() == DialogResult.OK) { string fpath = SaveFileDialog.FileName; byte[] data = rfe.File.ExtractFile(rfe); if (ExportCompressCheckBox.Checked) { data = ResourceBuilder.Compress(data); } RpfResourceFileEntry rrfe = rfe as RpfResourceFileEntry; if (rrfe != null) //add resource header if this is a resource file. { data = ResourceBuilder.AddResourceHeader(rrfe, data); } if (data == null) { MessageBox.Show("Error extracting file! " + rfe.File.LastError); return; } try { File.WriteAllBytes(fpath, data); } catch (Exception ex) { MessageBox.Show("Error saving file! " + ex.ToString()); } } } private class SearchResult { public RpfFileEntry FileEntry { get; set; } public int Offset { get; set; } public int Length { get; set; } public SearchResult(RpfFileEntry entry, int offset, int length) { FileEntry = entry; Offset = offset; Length = length; } } private byte LowerCaseByte(byte b) { if ((b >= 65) && (b <= 90)) //upper case alphabet... { b += 32; } return b; } private void AddSearchResult(SearchResult result) { try { if (InvokeRequired) { Invoke(new Action(() => { AddSearchResult(result); })); } else { SearchResults.Add(result); SearchResultsListView.VirtualListSize = SearchResults.Count; } } catch { } } private void Search() { if (InProgress) return; if (ScannedFiles.Count == 0) { MessageBox.Show("Please scan the GTAV folder first."); return; } if (SearchTextBox.Text.Length == 0) { MessageBox.Show("Please enter a search term."); return; } string searchtxt = SearchTextBox.Text; bool hex = SearchHexRadioButton.Checked; bool casesen = SearchCaseSensitiveCheckBox.Checked || hex; bool bothdirs = SearchBothDirectionsCheckBox.Checked; string[] ignoreexts = null; byte[] searchbytes1; byte[] searchbytes2; int bytelen; if (!casesen) searchtxt = searchtxt.ToLowerInvariant(); //case sensitive search in lower case. if (SearchIgnoreCheckBox.Checked) { ignoreexts = SearchIgnoreTextBox.Text.Split(','); for (int i = 0; i < ignoreexts.Length; i++) { ignoreexts[i] = ignoreexts[i].Trim(); } } if (hex) { if (searchtxt.Length < 2) { MessageBox.Show("Please enter at least one byte of hex (2 characters)."); return; } try { bytelen = searchtxt.Length / 2; searchbytes1 = new byte[bytelen]; searchbytes2 = new byte[bytelen]; for (int i = 0; i < bytelen; i++) { searchbytes1[i] = Convert.ToByte(searchtxt.Substring(i * 2, 2), 16); searchbytes2[bytelen - i - 1] = searchbytes1[i]; } } catch { MessageBox.Show("Please enter a valid hex string."); return; } } else { bytelen = searchtxt.Length; searchbytes1 = new byte[bytelen]; searchbytes2 = new byte[bytelen]; //reversed text... for (int i = 0; i < bytelen; i++) { searchbytes1[i] = (byte)searchtxt[i]; searchbytes2[bytelen - i - 1] = searchbytes1[i]; } } SearchTextBox.Enabled = false; SearchHexRadioButton.Enabled = false; SearchTextRadioButton.Enabled = false; SearchCaseSensitiveCheckBox.Enabled = false; SearchBothDirectionsCheckBox.Enabled = false; SearchIgnoreCheckBox.Enabled = false; SearchIgnoreTextBox.Enabled = false; SearchButton.Enabled = false; SearchSaveResultsButton.Enabled = false; InProgress = true; AbortOperation = false; SearchResultsListView.VirtualListSize = 0; SearchResults.Clear(); int totfiles = TotalFileCount; int curfile = 0; Task.Run(() => { DateTime starttime = DateTime.Now; int resultcount = 0; for (int f = 0; f < ScannedFiles.Count; f++) { var rpffile = ScannedFiles[f]; //UpdateStatus(string.Format("Searching {0}/{1} : {2}", f, ScannedFiles.Count, rpffile.Path)); foreach (var entry in rpffile.AllEntries) { var duration = DateTime.Now - starttime; if (AbortOperation) { UpdateStatus(duration.ToString(@"hh\:mm\:ss") + " - Search aborted."); InProgress = false; SearchComplete(); return; } RpfFileEntry fentry = entry as RpfFileEntry; if (fentry == null) continue; curfile++; if (fentry.NameLower.EndsWith(".rpf")) { continue; } if (ignoreexts != null) { bool ignore = false; for (int i = 0; i < ignoreexts.Length; i++) { if (fentry.NameLower.EndsWith(ignoreexts[i])) { ignore = true; break; } } if (ignore) { continue; } } UpdateStatus(string.Format("{0} - Searching {1}/{2} : {3}", duration.ToString(@"hh\:mm\:ss"), curfile, totfiles, fentry.Path)); byte[] filebytes = fentry.File.ExtractFile(fentry); if (filebytes == null) continue; int hitlen1 = 0; int hitlen2 = 0; for (int i = 0; i < filebytes.Length; i++) { byte b = casesen ? filebytes[i] : LowerCaseByte(filebytes[i]); byte b1 = searchbytes1[hitlen1]; //current test byte 1 byte b2 = searchbytes2[hitlen2]; if (b == b1) hitlen1++; else hitlen1 = 0; if (hitlen1 == bytelen) { AddSearchResult(new SearchResult(fentry, (i - bytelen), bytelen)); resultcount++; hitlen1 = 0; } if (bothdirs) { if (b == b2) hitlen2++; else hitlen2 = 0; if (hitlen2 == bytelen) { AddSearchResult(new SearchResult(fentry, (i - bytelen), bytelen)); resultcount++; hitlen2 = 0; } } } } } var totdur = DateTime.Now - starttime; UpdateStatus(totdur.ToString(@"hh\:mm\:ss") + " - Search complete. " + resultcount.ToString() + " results found."); InProgress = false; SearchComplete(); }); } private void SearchComplete() { try { if (InvokeRequired) { Invoke(new Action(() => { SearchComplete(); })); } else { SearchTextBox.Enabled = true; SearchHexRadioButton.Enabled = true; SearchTextRadioButton.Enabled = true; SearchCaseSensitiveCheckBox.Enabled = SearchTextRadioButton.Checked; SearchBothDirectionsCheckBox.Enabled = true; SearchIgnoreCheckBox.Enabled = true; SearchIgnoreTextBox.Enabled = SearchIgnoreCheckBox.Checked; SearchButton.Enabled = true; SearchSaveResultsButton.Enabled = true; } } catch { } } private void SearchButton_Click(object sender, EventArgs e) { Search(); } private void SearchAbortButton_Click(object sender, EventArgs e) { AbortOperation = true; } private void SearchSaveResultsButton_Click(object sender, EventArgs e) { SaveFileDialog.FileName = "SearchResults.txt"; if (SaveFileDialog.ShowDialog() == DialogResult.OK) { string fpath = SaveFileDialog.FileName; StringBuilder sb = new StringBuilder(); sb.AppendLine("CodeWalker Search Results for \"" + SearchTextBox.Text + "\""); sb.AppendLine("[File path], [Byte offset]"); if (SearchResults != null) { foreach (var r in SearchResults) { sb.AppendLine(r.FileEntry.Path + ", " + r.Offset.ToString()); } } File.WriteAllText(fpath, sb.ToString()); } } private void SearchTextRadioButton_CheckedChanged(object sender, EventArgs e) { SearchCaseSensitiveCheckBox.Enabled = SearchTextRadioButton.Checked; } private void SearchHexRadioButton_CheckedChanged(object sender, EventArgs e) { } private void SearchResultsListView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { var item = new ListViewItem(); if (e.ItemIndex < SearchResults.Count) { SearchResult r = SearchResults[e.ItemIndex]; item.Text = r.FileEntry.Name; item.SubItems.Add(r.Offset.ToString()); item.Tag = r; } e.Item = item; } private void SearchResultsListView_SelectedIndexChanged(object sender, EventArgs e) { if (SearchResultsListView.SelectedIndices.Count == 1) { var i = SearchResultsListView.SelectedIndices[0]; if ((i >= 0) && (i < SearchResults.Count)) { var r = SearchResults[i]; SelectFile(r.FileEntry, r.Offset+1, r.Length); } else { SelectFile(null, -1, 0); } } else { SelectFile(null, -1, 0); } } private void SearchIgnoreCheckBox_CheckedChanged(object sender, EventArgs e) { SearchIgnoreTextBox.Enabled = SearchIgnoreCheckBox.Checked; } private void SelTexturesListView_SelectedIndexChanged(object sender, EventArgs e) { Texture tex = null; if (SelTexturesListView.SelectedItems.Count == 1) { tex = SelTexturesListView.SelectedItems[0].Tag as Texture; } ShowTextureMip(tex, 0, false); } private void SelTextureMipTrackBar_Scroll(object sender, EventArgs e) { Texture tex = null; if (SelTexturesListView.SelectedItems.Count == 1) { tex = SelTexturesListView.SelectedItems[0].Tag as Texture; } SelTextureMipLabel.Text = SelTextureMipTrackBar.Value.ToString(); ShowTextureMip(tex, SelTextureMipTrackBar.Value, true); } private void ShowLargeFileContentsCheckBox_CheckedChanged(object sender, EventArgs e) { SelectFile(); } private void FlattenStructureCheckBox_CheckedChanged(object sender, EventArgs e) { FlatStructure = FlattenStructureCheckBox.Checked; if (InProgress) return; if (ScannedFiles.Count == 0) return; Cursor = Cursors.WaitCursor; SearchTextBox.Clear(); ClearFiles(); foreach (RpfFile file in RootFiles) { AddScannedFile(file, null); } Cursor = Cursors.Default; } } }