/*
 * By Burak (burak@burak.fr)
 */

var fs = require('fs');
var recursive = require('recursive-readdir');
var exec = require('child_process').exec;
var path = require('path');
var rmdir = require('rimraf');

window.ondragover = window.ondrop = function(e) { e.preventDefault(); return false; }
process.chdir('workdirectory/');

var el = document.querySelector('#drop');
el.ondragover = function(){
	updateGUIclassName("hover");
	updateGUIMessage("Drop the file");
	return false;
}
el.ondragleave = function(){
	updateGUIclassName("");
	updateGUIMessage("Drop your Habbo.swf here !");
	return false;
}
el.ondrop = function(e){
	e.preventDefault();

	for(var i = 0; i < e.dataTransfer.files.length; i++){
		var file = e.dataTransfer.files[i].path;
		global.output = path.dirname(file) + path.sep + "Cracked.swf";

		updateGUIMessage("Cracking SWF..");

		copyFile(file, "Work.swf", function(){
			abcexport("Work.swf");
		});

		global.effect = setInterval(function(){
			invertClassName();
		}, 500);
	}
}

function updateGUIMessage(message){
	var el = document.querySelector('#drop');
	el.innerHTML = message;
}

function updateGUIclassName(className){
	var el = document.querySelector('#drop');
	el.className = className;
}

function invertClassName(){
	var el = document.querySelector('#drop');
	if(el.className == "hover"){
		el.className = "";
	}else{
		el.className = "hover";
	}
}

function copyFile(source, target, cb) {
  var cbCalled = false;

  var rd = fs.createReadStream(source);
  rd.on("error", function(err) {
    done(err);
  });
  var wr = fs.createWriteStream(target);
  wr.on("error", function(err) {
    done(err);
  });
  wr.on("close", function(ex) {
    done();
  });
  rd.pipe(wr);

  function done(err) {
    if (!cbCalled) {
      cb(err);
      cbCalled = true;
    }
  }
}

function abcexport(file){
	exec('abcexport '+file, function(){
		rabcdasm(file.replace(".swf", "-0.abc"));
		rabcdasm(file.replace(".swf", "-1.abc"));
		rabcdasm(file.replace(".swf", "-2.abc"), true);
	});
}

function rabcdasm(abc, end){
	exec('rabcdasm '+abc, function(){
		if(end){
			searchFiles(abc.replace("-2.abc", ""));
		}
	});
}

function searchFiles(folder){
	global.needFiles = ["Habbo.class.asasm", "HabboCommunicationDemo.class.asasm", "HabboCommunicationManager.class.asasm"];

	global.foundFiles = [];

	recursive('./'+folder+'-0', ['*.script.*'], function (err, files) {
	  checkFilesRecursive(files);
	});
	recursive('./'+folder+'-1', ['*.script.*'], function (err, files) {
	  checkFilesRecursive(files);
	});
	recursive('./'+folder+'-2', ['*.script.*'], function (err, files) {
	  checkFilesRecursive(files);
	});

	global.check1 = setInterval(function(){
		if(global.foundFiles.length == global.needFiles.length){
			patchFiles(global.foundFiles);
			clearTimeout(global.check1);
		}
	}, 1000);
}

function checkFilesRecursive(files){
	files.forEach(function(file){
		var filename = cleanPath(file);

		global.needFiles.forEach(function(needed){
			if(needed == filename){
				global.foundFiles.push(file);
			}
		});
	});
}

function patchFiles(files){
	global.patch = 0;

	files.forEach(function(file){
		var filename = cleanPath(file);

		if(filename == global.needFiles[0]){
			patchDomainValidator(file);
		}else if(filename == global.needFiles[1]){
			patchRSAKey(file);
		}else if(filename == global.needFiles[2]){
			patchConnectionHost(file);
		}
	});

	global.check2 = setInterval(function(){
		if(global.patch == 3){
			recompileSWF();
			clearTimeout(global.check2);
		}
	}, 1000);
}

function cleanPath(path){
	var split = path.split("\\");
	return split[split.length-1];
}

function patchDomainValidator(file){
	var data = getFile(file);
	var split = data.split("\n");
	split.reverse();

	for(var i = 0; i < split.length; i++){
		var line = split[i];
		if(endsWith(line, 'returnvalue')){
			split.splice(i+1, 0, line.replace('returnvalue', 'pushtrue'));

			break;
		}
	}

	split.reverse();

	for(var j = 0; j < split.length; j++){
		var line = split[j];
		if(endsWith(line, 'pushscope')){
			split.splice(j-1, 1);
			split.splice(j-2, 1);
			split.splice(j-3, 1);
			split.splice(j-4, 1);
			split.splice(j-5, 1);
			split.splice(j-6, 1);
			split.splice(j-7, 1);
			break;
		}
	}

	putFile(file, replaceAll('PackageNamespace("", "#0")', 'PackageNamespace("")', split.join("\n")));
	global.patch++;
}

function patchRSAKey(file){
	var data = getFile(file);
	var split = data.split("\n");
	split.reverse();

	for(var i = 0; i < split.length; i++){
		var line = split[i];
		if(endsWith(line, '"RSAKey")')){
			split[i-1] = '      pushstring          "86851DD364D5C5CECE3C883171CC6DDC5760779B992482BD1E20DD296888DF91B33B936A7B93F06D29E8870F703A216257DEC7C81DE0058FEA4CC5116F75E6EFC4E9113513E45357DC3FD43D4EFAB5963EF178B78BD61E81A14C603B24C8BCCE0A12230B320045498EDC29282FF0603BC7B7DAE8FC1B05B52B2F301A9DC783B7"';
			split[i-3] = '      pushstring          "3"';

			split.splice(i-2, 1);
			split.splice(i-4, 1);

			break;
		}
	}

	split.reverse();

	putFile(file, split.join("\n"));
	global.patch++;
}

function patchConnectionHost(file){
	var data = getFile(file);
	var split = data.split("\n");

	for(var i = 0; i < split.length; i++){
		var line = split[i];
		if(endsWith(line, 'getlocal0') && endsWith(split[i+1], 'getlocal            6')){
			split[i+1] = '      findpropstrict      QName(PackageNamespace(""), "getProperty")';

			split.splice(i+2, 0, '      pushstring          "connection.info.host"');
			split.splice(i+3, 0, '      callproperty        QName(PackageNamespace(""), "getProperty"), 1');

			break;
		}
	}

	for(var j = 0; j < split.length; j++){
		var line = split[j];
		if(endsWith(line, 'newarray            5') && endsWith(split[j+5], 'newarray            3') && endsWith(split[j+9], 'newarray            2')){
			split[j-1] = '      pushint             65290';
			split[j-2] = '      pushint             65290';
			split[j-3] = '      pushint             65290';
			split[j-4] = '      pushint             65290';
			split[j-5] = '      pushint             65290';

			split[j+2] = '      pushint             65290';
			split[j+3] = '      pushint             65290';
			split[j+4] = '      pushint             65290';

			split[j+7] = '      pushint             65290';
			split[j+8] = '      pushint             65290';
		}
	}
	
	putFile(file, split.join("\n"));
	global.patch++;
}

function recompileSWF(){
	updateGUIMessage("Recompiling SWF..");
	exec('rabcasm Work-0/Work-0.main.asasm', function(){
		exec('rabcasm Work-1/Work-1.main.asasm', function(){
			exec('rabcasm Work-2/Work-2.main.asasm', function(){
				exec('abcreplace Work.swf 0 Work-0/Work-0.main.abc', function(){
					exec('abcreplace Work.swf 1 Work-1/Work-1.main.abc', function(){
						exec('abcreplace Work.swf 2 Work-2/Work-2.main.abc', function(){
							rmdir('Work-0/', function(){
								rmdir('Work-1/', function(){
									rmdir('Work-2/', function(){
										fs.unlink('Work-0.abc', function(){
											fs.unlink('Work-1.abc', function(){
												fs.unlink('Work-2.abc', function(){
													exec('swflzmacompress Work.swf', function(){
														copyFile('Work.swf', global.output, function(){
															fs.unlinkSync('Work.swf');
															updateGUIMessage("Finished ! Cracked :)");
															clearTimeout(global.effect);
														});
													});
												});
											});
										});
									});
								});
							});
						});
					});
				});
			});
		});
	});
}

function getFile(filename){
	return fs.readFileSync(filename, 'UTF-8');
}

function putFile(filename, data){
	return fs.writeFileSync(filename, data, 'UTF-8');
}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

function replaceAll(find, replace, str) {
  var find = find.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  return str.replace(new RegExp(find, 'g'), replace);
}