Odd.
There is an application called
hakpacker or something
Found the source on the vault.
While it is able to read and write nwn1 and nwn2 haks, when I copy the implimentation, it wrote a corrupted file inside the hak.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using OEIShared.IO.ERF;
using TaniTerrain;
using TaniWaterMill;
namespace ERFReader {
public partial class ERFinder : Form {
ERFFile myFile;
bool loaded = false;
bool messed = false;
bool stripped = false;
string[] cmdline;
public ERFinder(string[] args) {
InitializeComponent();
cmdline = args;
}
private bool watermaps() {
foreach (ListViewItem lvi in FileTable.Items)
if (((ERFResource)lvi.Tag).ResourceType == 2062)
return true;
return false;
}
private void updatebtns() {
BTN_Open.Enabled = !loaded;
BTN_New.Enabled = !loaded;
BTN_Import.Enabled = loaded;
BTN_Impor****er.Enabled = loaded && watermaps();
BTN_StripMod.Enabled = loaded && (myFile.Filename.ToLower().EndsWith(".mod")) && !stripped;
BTN_Export.Enabled = (FileTable.SelectedItems.Count > 0);
BTN_Remove.Enabled = (FileTable.SelectedItems.Count > 0);
if (loaded)
BTN_Save.Enabled = (myFile.Filename != "") && messed && !stripped;
else
BTN_Save.Enabled = false;
BTN_SaveAs.Enabled = loaded;
BTN_Close.Enabled = loaded;
cb_v10.Enabled = loaded;
BTN_Exit.Enabled = true;
if (loaded) {
DateTime dt = new DateTime((int)(1900 + myFile.FileHeader.BuildYear), 1, 1);
dt = dt.AddDays((double)myFile.FileHeader.BuildDay - 1.0);
LBL_Status.Text = myFile.Filename + " [" + myFile.FileHeader.FileType + " " + myFile.FileHeader.FileVersion + "] " + myFile.Resources.Count + " files";
} else
LBL_Status.Text = "no file loaded";
}
private ListViewItem FileTableItem(ERFResource r) {
ListViewItem l = new ListViewItem(new string[] { r.FullName.ToLower(), r.Extension.ToLower(), r.ResourceSize.ToString() });
l.Text = r.FullName.ToLower();
l.Name = r.FullName.ToLower();
l.Tag = r;
l.ToolTipText = r.FullName.ToLower() + " (" + r.ResourceSize.ToString() + " bytes)";
return l;
}
private void updatelist() {
FileTable.Items.Clear();
if (loaded) {
foreach (ERFResource r in myFile.Resources)
FileTable.Items.Add(FileTableItem(r));
} else LBL_Status.Text = "";
updatebtns();
}
private void LoadERF(string fn) {
if (loaded) myFile.Dispose();
try {
myFile = new ERFFile(fn, true);
messed = false;
loaded = true;
stripped = false;
cb_v10.Checked = (myFile.FileHeader.Has16CharResrefs);
} catch (Exception ex) {
MessageBox.Show("file loading failed: " + ex.Message);
myFile.Dispose();
loaded = false;
messed = false;
stripped = false;
}
updatelist();
}
private void BTN_Open_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
if (FileOpen.ShowDialog() == DialogResult.OK)
LoadERF(FileOpen.FileName);
this.Enabled = true;
}
string exportfolder = "";
private void BTN_Export_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
try {
if (exportfolder != "")
Folder.SelectedPath = exportfolder;
if (loaded)
if (Folder.ShowDialog() == DialogResult.OK) {
foreach (ListViewItem lvi in FileTable.SelectedItems) {
ERFResource r = (ERFResource)lvi.Tag;
r.SaveData(Folder.SelectedPath + "\\\\" + r.FullName);
}
exportfolder = Folder.SelectedPath;
}
} catch (Exception ex) {
MessageBox.Show("ooops! something went wrong: " + ex.Message, "nasty bug detected!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
this.Enabled = true;
}
private ERFResource FileTableContains(string filename) {
foreach (ListViewItem lvi in FileTable.Items)
if (((ERFResource)lvi.Tag).FullName.ToLower() == filename.ToLower())
return (ERFResource)lvi.Tag;
return null;
}
private void ResourceRemover(string fullname) {
List<ERFResource> l = new List<ERFResource>();
foreach (ERFResource r in myFile.Resources)
if (fullname.ToLower().EndsWith(r.FullName.ToLower()))
l.Add(r);
foreach (ERFResource r in l)
myFile.Resources.Remove(r);
}
void importFiles(string[] names) {
foreach (string f in names) {
ERFResource newres = new ERFResource();
newres.DiskFilename = f;
if (FileTable.Items.Find(newres.FullName.ToLower(), false).Length > 0)
switch (MessageBox.Show("File already exists: " + newres.FullName + "n\\r\\n\\rStill import?", "file exists", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning)) {
case DialogResult.Yes:
ResourceRemover(f);
myFile.Resources.Add(newres);
messed = true;
break;
case DialogResult.Cancel:
return;
} else {
myFile.Resources.Add(newres);
messed = true;
}
}
updatelist();
}
private void BTN_Import_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
if (loaded) {
if (FileImport.ShowDialog() == DialogResult.OK) {
importFiles(FileImport.FileNames);
}
}
this.Enabled = true;
}
private void BTN_Save_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
try {
if (loaded && myFile.Filename != "") {
string fn = myFile.Filename;
FileStream fs = new FileStream(fn + ".tmp", FileMode.Create);
myFile.Save(fs);
fs.Seek(7, SeekOrigin.Begin);
if (cb_v10.Checked) {
fs.WriteByte(48);
fs.Seek(myFile.FileHeader.OffsetToKeyList, SeekOrigin.Begin);
byte[] buf = new byte[(32 + 4 + 4) * myFile.FileHeader.FileCount];
fs.Read(buf, 0, (int)((32 + 4 + 4) * myFile.FileHeader.FileCount));
fs.Seek(myFile.FileHeader.OffsetToKeyList, SeekOrigin.Begin);
for (int n = 0; n < myFile.FileHeader.FileCount; n++) {
fs.Write(buf, n * (32 + 4 + 4), 16);
fs.Write(buf, n * (32 + 4 + 4) + 32, ;
}
} else {
fs.WriteByte(49);
}
fs.Close();
myFile.Dispose();
System.IO.File.Delete(fn);
System.IO.File.Move(fn + ".tmp", fn);
myFile = new ERFFile(fn, true);
messed = false;
stripped = false;
updatebtns();
}
} catch (Exception) {
MessageBox.Show("An error occurred while saving. Please check to see that the file is not already in use.");
}
this.Enabled = true;
}
private void BTN_Close_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
if (loaded) {
bool really = true;
if (messed)
if (MessageBox.Show("you haven't saved your changes. really close?", "caution! unsaved changes!", MessageBoxButtons.OKCancel) != DialogResult.OK)
really = false;
if (really) {
myFile.Dispose();
loaded = false;
messed = false;
stripped = false;
updatelist();
}
}
this.Enabled = true;
}
private void BTN_SaveAs_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
try {
if (loaded)
if (FileSave.ShowDialog() == DialogResult.OK) {
string fn = FileSave.FileName.ToLower();
string dbk = "";
if (fn.EndsWith(".hak"))
dbk = "HAK ";
else if (fn.EndsWith(".erf"))
dbk = "ERF ";
else if (fn.EndsWith(".mod"))
dbk = "MOD ";
else if (fn.EndsWith(".pwc"))
dbk = "ERF ";
if (dbk == "")
MessageBox.Show("sorry, you must select either .hak, .erf, .mod or .pwc as an extension!", "unknown extension selected");
else {
myFile.FileHeader.FileType = dbk;
myFile.FileHeader.BuildYear = (uint)(DateTime.Now.Year - 1900);
myFile.FileHeader.BuildDay = (uint)DateTime.Now.DayOfYear;
FileStream fs = new FileStream(fn, FileMode.Create);
myFile.Save(fs);
if (cb_v10.Checked) {
fs.Seek(7, SeekOrigin.Begin);
fs.WriteByte(48);
fs.Seek(myFile.FileHeader.OffsetToKeyList, SeekOrigin.Begin);
byte[] buf = new byte[(32 + 4 + 4) * myFile.FileHeader.FileCount];
fs.Read(buf, 0, (int)((32 + 4 + 4) * myFile.FileHeader.FileCount));
fs.Seek(myFile.FileHeader.OffsetToKeyList, SeekOrigin.Begin);
for (int n = 0; n < myFile.FileHeader.FileCount; n++) {
fs.Write(buf, n * (32 + 4 + 4), 16);
fs.Write(buf, n * (32 + 4 + 4) + 32, ;
}
} else {
fs.Seek(7, SeekOrigin.Begin);
fs.WriteByte(49);
}
fs.Close();
myFile.Filename = fn;
updatelist();
}
}
messed = false;
stripped = false;
} catch (Exception ex) {
MessageBox.Show("oooops! something went wrong: " + ex.Message, "nasty bug detected!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
updatebtns();
this.Enabled = true;
}
private void BTN_exit_Click(object sender, EventArgs e) {
bool really = true;
if (loaded && messed)
if (MessageBox.Show("you haven't saved your changes. really exit?", "caution! unsaved changes!", MessageBoxButtons.OKCancel) != DialogResult.OK)
really = false;
if (really)
this.Close();
}
private void BTN_NewHAK_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
if (loaded) myFile.Dispose();
myFile = new ERFFile();
myFile.FileHeader.FileType = " ";
myFile.FileHeader.BuildYear = (uint)(DateTime.Now.Year - 1900);
myFile.FileHeader.BuildDay = (uint)DateTime.Now.DayOfYear;
cb_v10.Checked = false;
loaded = true;
messed = false;
stripped = false;
updatelist();
this.Enabled = true;
}
private void BTN_Remove_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
if (loaded) {
if (FileTable.SelectedItems.Count > 0)
messed = true;
foreach (ListViewItem lvi in FileTable.SelectedItems)
myFile.Resources.Remove((ERFResource)lvi.Tag);
updatelist();
}
this.Enabled = true;
}
private void ERFinder_Load(object sender, EventArgs e) {
if (cmdline.Length > 0)
LoadERF(cmdline[0]);
else
updatelist();
}
private void BTN_Impor****er_Click(object sender, EventArgs e) {
this.Enabled = false; // true;
if (loaded) {
this.Enabled = false;
List<ERFResource> watermaps = new List<ERFResource>();
foreach (ERFResource watermap in myFile.Resources)
if (watermap.FullName.ToLower().EndsWith(".nwm"))
watermaps.Add(watermap);
foreach (ERFResource watermap in watermaps) {
WaterMillArea wma = new WaterMillArea(new MemoryStream(watermap.Data));
string t = watermap.FullName.ToLower().Replace(".nwm", "");
List<ERFResource> found_resources = new List<ERFResource>();
foreach (ERFResource r in myFile.Resources)
if (r.FullName.ToLower() == t + ".trn" || r.FullName.ToLower() == t + ".trx")
found_resources.Add(r);
foreach (ERFResource found in found_resources) {
TaniTRN wm_trn = new TaniTRN();
wm_trn.Load(new MemoryStream(found.Data));
myFile.Resources.Remove(found);
wm_trn.WaterRemoveAll();
foreach (TaniWM wm in wma.layers)
if (wm.wm_selected)
foreach (TaniWATR tw in wm.lwaters)
if (wm.wm_global_water) {
tw.Move(wm.wm_move_x, wm.wm_move_y, wm.wm_raisewater);
wm.ValuesToWATR(tw);
if (tw.AdjustFlagsToTreshold(wm.wm_treshold))
wm_trn.WaterAdd(tw);
} else wm_trn.WaterAdd(tw);
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
wm_trn.Save(bw);
found.Data = ms.ToArray();
myFile.Resources.Add(found);
messed = true;
}
}
updatebtns();
this.Enabled = true;
}
}
private int col = 0;
private int updown = +1;
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e) {
if (e.Column == col)
updown *= -1;
col = e.Column;
FileTable.ListViewItemSorter = new ListViewItemComparer(e.Column, updown);
}
private void FileTable_SelectedIndexChanged(object sender, EventArgs e) {
updatebtns();
}
private void BTN_StripMod_Click(object sender, EventArgs e) {
int module_size = 0;
int walkmesh_size = 0;
int walkmesh_oversize = 0;
this.Enabled = false; // true;
if (loaded && (MessageBox.Show("attention!\\r\\n\\r\\nthis will remove all data which is not needed for running on a standalone server. after stripping you can no more edit this file in toolset or run it inside the game. do not save this module to the original file, unless you know exactly what you are doing - you might loose your work!\\r\\n\\r\\ndo you really want to continue?", "warning!", MessageBoxButtons.OKCancel) == DialogResult.OK)) {
this.Enabled = false;
List<ERFResource> remres = new List<ERFResource>();
List<ERFResource> trxs = new List<ERFResource>();
foreach (ERFResource r in myFile.Resources) {
if (r.FullName.ToLower().EndsWith(".trn") || r.FullName.ToLower().EndsWith(".nwm"))
remres.Add(r);
if (r.FullName.ToLower().EndsWith(".trx"))
trxs.Add(r);
}
foreach (ERFResource r in remres)
myFile.Resources.Remove(r);
foreach (ERFResource r in trxs) {
TaniTRN trx = new TaniTRN();
trx.Load(new MemoryStream(r.Data));
myFile.Resources.Remove(r);
walkmesh_size += trx.m_cASWM.len_uncomp;
walkmesh_oversize += trx.m_cASWM.len_uncomp - trx.m_cASWM.len_comp;
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
trx.SaveStripped(bw);
r.Data = ms.ToArray();
myFile.Resources.Add(r);
messed = true;
stripped = true;
}
foreach (ERFResource r in myFile.Resources)
module_size += (int)r.ResourceSize;
int size_in_mem_min = 50 * 1024 * 1024 + 3 * walkmesh_size;
int size_in_mem_max = 70 * 1024 * 1024 + 5 * walkmesh_size;
MessageBox.Show("your module was stripped of all unneeded data, new size is " + ((int)(module_size / (1024 * 1024))).ToString() + " mb (" + module_size.ToString() + " bytes); but don't let the new, smaller size fool you: uncompressed walkmesh sums up to " + ((int)(walkmesh_size / (1024 * 1024))).ToString() + " mb (" + walkmesh_size.ToString() + " bytes), which must be loaded to memory! my best guess is that your module will use between " + ((int)((size_in_mem_min) / (1024 * 1024))).ToString() + " and " + ((int)((size_in_mem_max) / (1024 * 1024))).ToString() + " mb in memory.", "stripping done");
updatelist();
}
this.Enabled = true;
}
private void OnDragEnter(object sender, DragEventArgs e) {
e.Effect = DragDropEffects.Copy;
}
private void OnDragDrop(object sender, DragEventArgs e) {
importFiles((string[])e.Data.GetData(DataFormats.FileDrop));
updatelist();
}
private void OnItemDrag(object sender, ItemDragEventArgs e) {
//string color = strTables;
if (FileTable.SelectedItems.Count == 0)
return;
List<String> files = new List<String>();
String temp = System.IO.Path.GetTempPath();
foreach (ListViewItem lvi in FileTable.SelectedItems) {
ERFResource r = (ERFResource)lvi.Tag;
r.SaveData(temp + r.FullName);
files.Add(temp + r.FullName);
}
// WriteFile(fileName, sbCSV);
string[] list = files.ToArray();
DataObject data = new DataObject(DataFormats.FileDrop, files.ToArray());
FileTable.DoDragDrop(data, DragDropEffects.Move);
}
private void OnQueryContinueDrag(object sender, QueryContinueDragEventArgs e) { }
private void OnMouseClick(object sender, MouseEventArgs e) {}
private void BTN_selall_Click(object sender, EventArgs e)
{
foreach (ListViewItem lvi in FileTable.Items)
lvi.Selected = true;
}
private void BTN_selnone_Click(object sender, EventArgs e)
{
foreach (ListViewItem lvi in FileTable.Items)
lvi.Selected = false;
}
private void BTN_selinvert_Click(object sender, EventArgs e)
{
foreach (ListViewItem lvi in FileTable.Items)
lvi.Selected = !lvi.Selected;
}
}
class ListViewItemComparer : System.Collections.IComparer {
private int col;
private int updown;
public ListViewItemComparer(int column, int up) {
col = column;
updown = up;
}
public int Compare(object x, object y) {
switch (col) {
case 2:
return updown * ((int)(((ERFResource)((ListViewItem)x).Tag).ResourceSize - ((ERFResource)((ListViewItem)y).Tag).ResourceSize));
default:
return updown * (String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text));
}
}
}
}