// toee_level_cap_remover.js
// Level Cap Remover for Temple of Elemental Evil, Version 0.11
// Requires TOEE with 2nd Patch applied.
//
// Raises the maximum PC level to 20.
//
// Modifies the file temple.dll in the TOEE install directory.

// byte offset in temple.dll, original byte, new byte
var patchArray = [
		  [ 0x00080255, 0x0a, 0x14 ],
		  [ 0x00080267, 0xb8, 0x04 ],
		  [ 0x00080268, 0xbf, 0xaf ],
		  [ 0x00080269, 0x2c, 0x2a ],
		  [ 0x0008029e, 0x0a, 0x14 ],
		  [ 0x000802a2, 0xe0, 0x54 ],
		  [ 0x000802a3, 0xbf, 0xaf ],
		  [ 0x000802a4, 0x2c, 0x2a ],
		  [ 0x000802ab, 0xbc, 0x08 ],
		  [ 0x000802ac, 0xbf, 0xaf ],
		  [ 0x000802ad, 0x2c, 0x2a ],
		  [ 0x000802ed, 0x0b, 0x15 ],
		  [ 0x000802f1, 0x0b, 0x15 ],
		  [ 0x000802f8, 0xb4, 0x00 ],
		  [ 0x000802f9, 0xbf, 0xaf ],
		  [ 0x000802fa, 0x2c, 0x2a ],
		  [ 0x00080306, 0xc8, 0x30 ],
		  [ 0x00080307, 0xaf, 0xe6 ],
		  [ 0x00080308, 0x00, 0x02 ],
		  [ 0x0008030d, 0x0b, 0x15 ],
		  [ 0x00080313, 0x0a, 0x14 ],
		  [ 0x0008031a, 0xb8, 0x04 ],
		  [ 0x0008031b, 0xbf, 0xaf ],
		  [ 0x0008031c, 0x2c, 0x2a ],
		  [ 0x000b6006, 0xe8, 0xe9 ],
		  [ 0x000b6007, 0xf5, 0x95 ],
		  [ 0x000b6008, 0xe7, 0x5f ],
		  [ 0x000b6009, 0xfb, 0x1b ],
		  [ 0x000b600a, 0xff, 0x00 ],
		  [ 0x000eb784, 0xec, 0x00 ],
		  [ 0x000eb785, 0xbe, 0xaf ],
		  [ 0x000eb786, 0x28, 0x2a ],
		  [ 0x0026bfa0, 0x00, 0xe8 ],
		  [ 0x0026bfa1, 0x00, 0x5b ],
		  [ 0x0026bfa2, 0x00, 0x88 ],
		  [ 0x0026bfa3, 0x00, 0xe0 ],
		  [ 0x0026bfa4, 0x00, 0xff ],
		  [ 0x0026bfa5, 0x00, 0x3c ],
		  [ 0x0026bfa6, 0x00, 0x0a ],
		  [ 0x0026bfa7, 0x00, 0x7e ],
		  [ 0x0026bfa8, 0x00, 0x02 ],
		  [ 0x0026bfa9, 0x00, 0xb0 ],
		  [ 0x0026bfaa, 0x00, 0x0a ],
		  [ 0x0026bfab, 0x00, 0xe9 ],
		  [ 0x0026bfac, 0x00, 0x5b ],
		  [ 0x0026bfad, 0x00, 0xa0 ],
		  [ 0x0026bfae, 0x00, 0xe4 ],
		  [ 0x0026bfaf, 0x00, 0xff ],
		  [ 0x002aaf08, 0x00, 0xe8 ],
		  [ 0x002aaf09, 0x00, 0x03 ],
		  [ 0x002aaf0c, 0x00, 0xb8 ],
		  [ 0x002aaf0d, 0x00, 0x0b ],
		  [ 0x002aaf10, 0x00, 0x70 ],
		  [ 0x002aaf11, 0x00, 0x17 ],
		  [ 0x002aaf14, 0x00, 0x10 ],
		  [ 0x002aaf15, 0x00, 0x27 ],
		  [ 0x002aaf18, 0x00, 0x98 ],
		  [ 0x002aaf19, 0x00, 0x3a ],
		  [ 0x002aaf1c, 0x00, 0x08 ],
		  [ 0x002aaf1d, 0x00, 0x52 ],
		  [ 0x002aaf20, 0x00, 0x60 ],
		  [ 0x002aaf21, 0x00, 0x6d ],
		  [ 0x002aaf24, 0x00, 0xa0 ],
		  [ 0x002aaf25, 0x00, 0x8c ],
		  [ 0x002aaf28, 0x00, 0xc8 ],
		  [ 0x002aaf29, 0x00, 0xaf ],
		  [ 0x002aaf2c, 0x00, 0xd8 ],
		  [ 0x002aaf2d, 0x00, 0xd6 ],
		  [ 0x002aaf30, 0x00, 0xd0 ],
		  [ 0x002aaf31, 0x00, 0x01 ],
		  [ 0x002aaf32, 0x00, 0x01 ],
		  [ 0x002aaf34, 0x00, 0xb0 ],
		  [ 0x002aaf35, 0x00, 0x30 ],
		  [ 0x002aaf36, 0x00, 0x01 ],
		  [ 0x002aaf38, 0x00, 0x78 ],
		  [ 0x002aaf39, 0x00, 0x63 ],
		  [ 0x002aaf3a, 0x00, 0x01 ],
		  [ 0x002aaf3c, 0x00, 0x28 ],
		  [ 0x002aaf3d, 0x00, 0x9a ],
		  [ 0x002aaf3e, 0x00, 0x01 ],
		  [ 0x002aaf40, 0x00, 0xc0 ],
		  [ 0x002aaf41, 0x00, 0xd4 ],
		  [ 0x002aaf42, 0x00, 0x01 ],
		  [ 0x002aaf44, 0x00, 0x40 ],
		  [ 0x002aaf45, 0x00, 0x13 ],
		  [ 0x002aaf46, 0x00, 0x02 ],
		  [ 0x002aaf48, 0x00, 0xa8 ],
		  [ 0x002aaf49, 0x00, 0x55 ],
		  [ 0x002aaf4a, 0x00, 0x02 ],
		  [ 0x002aaf4c, 0x00, 0xf8 ],
		  [ 0x002aaf4d, 0x00, 0x9b ],
		  [ 0x002aaf4e, 0x00, 0x02 ],
		  [ 0x002aaf50, 0x00, 0x30 ],
		  [ 0x002aaf51, 0x00, 0xe6 ],
		  [ 0x002aaf52, 0x00, 0x02 ],
		  [ 0x002aaf54, 0x00, 0x50 ],
		  [ 0x002aaf55, 0x00, 0x34 ],
		  [ 0x002aaf56, 0x00, 0x03 ],
		  [ 0x002aaffd, 0x00, 0x44 ],
		  [ 0x002aaffe, 0x00, 0x57 ],
		  [ 0x002aafff, 0x00, 0x47 ],
		  [ 0x00348000, -1, -1 ] // Must not have a trailing comma
		  ];

function assert_(expr, msg) {
  if (!expr) {
    WScript.Echo("Internal Error: " + msg + ": installation aborted");
    WScript.Quit(1);
  }
}

function PatchTempleDLL(patchArray) {
  dllName = "temple.dll";

  var wshShell = WScript.CreateObject("WScript.Shell");
  var intro = ("TOEE Level Cap Remover Version 0.11\n\n" +
	       "This script will modify the file " +
               dllName + ".\n\n" +
               "If you have an anti-virus or firewall program running, " +
               "it will probably complain that this script is about to " +
               "perform a suspicious or malicious activity.  In this case, " +
               "allow it, although in general, you shouldn't.\n\n" +
               "Patching will take a few seconds.");
  var button = wshShell.Popup(intro,
			      0, // Wait for user input
			      "TOEE Level Cap Remover 0.11",
			      1|32 // OK and Cancel buttons, Question Mark icon
			      );
  if (button != 1) { // 1 == OK
    WScript.Echo("Installation cancelled.  Have a nice day.");
    WScript.Quit();
  }
  
  // Find install dir via registry
  var installDir = wshShell.RegRead("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AD80F06B-0F21-4EEE-934D-BEF0D21E6383}\\InstallLocation");
  assert_(installDir != "", "installDir != \"\"");

  // Start file operations (do this after intro message)
  var fso = WScript.CreateObject("Scripting.FileSystemObject");
  var ForReading = 1, ForWriting = 2, ForAppending = 8;
  var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;

  if (!fso.FolderExists(installDir)) {
    WScript.Echo("TOEE installation directory " +
		 installDir + 
		 " doesn't exist: installation aborted");
    WScript.Quit();
  }

  // Open temple.dll
  var templeFilename = installDir + "\\" + dllName;
  var templeOrigFilename = templeFilename + ".orig";
  if (!fso.FileExists(templeFilename)) {
    WScript.Echo("DLL file " +
		 templeFilename + 
		 " doesn't exist: installation aborted");
    WScript.Quit();
  }
  var templeFile = fso.GetFile(templeFilename);
  var templeSize = 3440640;
  if (templeFile.Size != templeSize) {
    WScript.Echo("Version Mismatch in temple.dll (size was " +
		 templeFile.Size + "): installation aborted");
    WScript.Quit(1);
  }
  if (fso.FileExists(templeOrigFilename)) {
    WScript.Echo("Backup file " +
		 templeOrigFilename + 
		 " already exists: installation aborted");
    WScript.Quit(1);
  }

  // The JScript interpreter is lame, and doesn't handle string
  // literals in the range 0x80 to 0x9f correctly.  So we read the
  // required literals from temple.dll.  Dumb.
  stringLiteralArray = [];
  for (i = 0; i < 256; ++i) {
    stringLiteralArray[i] = String.fromCharCode(i);
  }
  {
    var templeStream2 = templeFile.OpenAsTextStream(ForReading, 
						    TristateFalse // ASCII
						    );
    templeStream2.Skip(0x10e1);
    stringLiteralArray[0x9b] = templeStream2.Read(1);
    templeStream2.Skip(0x15f); // 0x1241
    stringLiteralArray[0x9a] = templeStream2.Read(1);
    templeStream2.Skip(0x62c); // 0x186e
    stringLiteralArray[0x98] = templeStream2.Read(1);
    templeStream2.Skip(0x1d94); // 0x3603
    stringLiteralArray[0x95] = templeStream2.Read(1);
    templeStream2.Skip(0x212); // 0x3816
    stringLiteralArray[0x88] = templeStream2.Read(1);
    templeStream2.Skip(0x887); // 0x409e
    stringLiteralArray[0x8c] = templeStream2.Read(1);
    templeStream2.Close()
  }

  // Read bytes from temple.dll
  var templeStream = templeFile.OpenAsTextStream(ForReading, 
						 TristateFalse // ASCII
						 );

  // Create temp file for patched version of temple.dll
  var outFilename = installDir + "\\" + fso.GetTempName();
  var outStream = fso.OpenTextFile(outFilename,
                                   ForWriting,
                                   true, // create
                                   TristateFalse // ASCII
                                   );

  // Read from temple, patch, write to output
  var byteOffset = 0; // Offset in file
  var patchIdx = 0; // Offset in patch array
  var alreadyPatched = 0;
  var appliedPatch = 0
  var versionError = 0;
  while (patchIdx < patchArray.length) {
    var curPatch = patchArray[patchIdx];
    
    // Read bytes up to patch byte
    var bytesToRead = (curPatch[0] - byteOffset);
    assert_(bytesToRead >= 0, "bytesToRead >= 0");
    if (bytesToRead > 0) {
      var data = templeStream.Read(bytesToRead);
      assert_(data.length == bytesToRead, "data.length == bytesToRead");
      outStream.write(data);
      byteOffset += bytesToRead;
    }
        
    // Read patch byte
    if (curPatch[1] != -1) {
      var data = templeStream.Read(1);
      assert_(data.length == 1, "data.length == 1");
      var byte = data.charCodeAt(0);
      
      // Patch this byte?
      if (byte == curPatch[1]) {
	data = stringLiteralArray[curPatch[2]];
	assert_(data.length == 1, "data.length == 1");
	appliedPatch = 1;
      } else if (byte == stringLiteralArray[curPatch[2]].charCodeAt(0)) {
	alreadyPatched = 1;
      } else {
	if (versionError != 1) {
	  WScript.Echo("Mismatch at byteOffset: " + byteOffset +
		       ", patchIdx: " + patchIdx +
		       ", bytes are: " + byte + " " + 
		       curPatch[1] + " " + curPatch[2] + " ");
	}
	versionError = 1;
      }
    
      outStream.Write(data);
      byteOffset += 1;
      //var outFile = fso.GetFile(outFilename);
      //assert_(outFile.Size == byteOffset, " " + byteOffset + " " + outFile.Size);
    }
    
    patchIdx += 1;
  }

  templeStream.Close();
  outStream.Close();

  if ((versionError == 1) ||
      (alreadyPatched == 1 && appliedPatch == 1)) {
    WScript.Echo("Version Mismatch in temple.dll: installation aborted");
  } else if (alreadyPatched == 1) {
    WScript.Echo("Level Cap Remover Already Applied: No action taken");
  } else{
    // Shuffle Files around
    fso.MoveFile(templeFilename, templeOrigFilename);
    fso.MoveFile(outFilename, templeFilename);

    WScript.Echo("TOEE Level Cap Remover successfully installed.\n\n" +
		 "To uninstall, delete the modified temple.dll " +
		 "and rename the file \n    " +
		 templeOrigFilename + "\nto\n    " +
		 templeFilename);
  }

  if (fso.FileExists(outFilename)) {
    fso.DeleteFile(outFilename);
  }
}

PatchTempleDLL(patchArray);

