Jump to content

Wow, I've Written A Lot Of Programs To Do With Lego Racers...


dead_name
 Share

Recommended Posts

36 programs is a lot indeed. That just might be more than what LR1 was built with!

I see LSB files (which I know myself are the loading screens), MIB files (any more progress on that?), .JAM extractor/creator (We have that already, but you may be planning something), ImageToLRBMP? I can guess what that is. Are you any closer to cracking the compression? HD_Mod_Pack. So, you're making a mod pack already? :P

Link to comment
Share on other sites

Full List:

BinaryFileDump - First iteration of the generic file dumper

BinaryFileDump2 - Second iteration of the generic file dumper

BinaryFileDump3 - Third (and probably final) iteration of the generic file dumper

BitmapCheck - Used for testing a theory about the BMP headers

BitmapFromGif - Converts a GIF image to a LR1 BMP

BitmapGen - Generates a LR1 BMP that contains a mathematical pattern

BitmapPalette - Shows the color palette of a given bitmap

Block0704Dump - Very early research to do with the generic binary files

BVB_Research - Research into the BVB files (collision meshes)

CPB_Analyse - Checks that a given CPB file loads correctly

CPB_Map - Renders a 2D map for a given CPB (or attempts to at least, this was early and probably wrong)

CPB_Thing - Earlier research into the CPB format

FindCommonFiles - Lists all filenames that show up in every track folder

FormatList - Generates a list of all filename extensions used by the game, and how often each occurs

GDB_Analyse - Checks that a given GDB file loads correctly. First 100%-working GDB parsing code.

GDB_Flops_Dump - Very first piece of research into the GDB format.

GDB_Mapper - Abandoned 2D mapper for GDB models

GDB_PointCloud - 3D point-cloud renderer for GDB files. Created prior to me working out the polygon format.

GDB_Viewer - Early 3D viewer for GDB models.

GDB_Viewer_2 - Newer (and from a code perspective, cleaner) 3D GDB viewer. Available publicly on RRU.

HD_Mod_Pack - Very old, only patches some variables in MAINMENU.MIB.

HeaderDumper - Early generic format research

ImageToLRBMP - Converts any given image to a GIF and then to a LR1 BMP.

JAMpack - Abandoned JAM recompiler

JAMunpack - JAM extractor

LibLegoRacers - Code library containing classes for several of the file formats. Referenced by several of the other projects.

LoadscreenLSB - Early research into the LSB format.

LSB_Analyse - Checks that a given LSB file loads correctly

MIB_Analyse - Checks that a given MIB file loads correctly. Incomplete, a few valid files fail to load.

MIB_Dump - Abandoned MIB dumper. Replaced by the generic file dumper.

PWB_Dump - Renders a map of powerups from a given PWB file

PWB_Grid_Gen - Generates a grid of powerups as a PWB file, to be inserted into the game for fun.

RCB_Analyse - Checks that a given RCB file loads correctly

SKB_Analyse - Checks that a given SKB file loads correctly

SRF_Analyse - Checks that a given SRF file loads correctly

Thanks to recent developments (in particular on BinaryFileDump3), a lot of these projects will be completely rewritten.

Link to comment
Share on other sites

JrMasterModelBuilder

That's impressive! I see you abandoned your JAM recompiler. If you want, I can give you the source to mine. ActionScript might be a little tricky to convert to C#, but I imagine it C# would run faster.

Link to comment
Share on other sites

All you need to do is make one program to act as a sort of UI which the user types information into, then the program opens up the tool and passes all the arguments to that tool, and you can put these all together in the one Toolkit Suite.

Link to comment
Share on other sites

That's impressive! I see you abandoned your JAM recompiler. If you want, I can give you the source to mine. ActionScript might be a little tricky to convert to C#, but I imagine it C# would run faster.

That would be great, thank you :D

All you need to do is make one program to act as a sort of UI which the user types information into, then the program opens up the tool and passes all the arguments to that tool, and you can put these all together in the one Toolkit Suite.

Funny you should mention that..

Link to comment
Share on other sites

All you need to do is make one program to act as a sort of UI which the user types information into, then the program opens up the tool and passes all the arguments to that tool, and you can put these all together in the one Toolkit Suite.

Funny you should mention that..

Yea. That's interesting that you mentioned that, Extreme. Cyrem has already told me origamiguy is making a universial viewer/converter/creator/whateverelseisneeded program for LR1. And that list above looks like it's just the start of that program. :P

Link to comment
Share on other sites

All you need to do is make one program to act as a sort of UI which the user types information into, then the program opens up the tool and passes all the arguments to that tool, and you can put these all together in the one Toolkit Suite.

Funny you should mention that..

Yea. That's interesting that you mentioned that, Extreme. Cyrem has already told me origamiguy is making a universial viewer/converter/creator/whateverelseisneeded program for LR1. And that list above looks like it's just the start of that program. :P

What Cyrem was talking about is a universal converter for the binary-coded files in the game. What I'm cryptically talking about is something else entirely that nobody has been told about.

Link to comment
Share on other sites

What I'm cryptically talking about is something else entirely that nobody has been told about.

The Death Star

Many Bothans died to bring us this information...

/offtopic

Link to comment
Share on other sites

What I'm cryptically talking about is something else entirely that nobody has been told about.

Oh god, he's going to put ads in the program.

;P

Link to comment
Share on other sites

What I'm cryptically talking about is something else entirely that nobody has been told about.

Oh god, he's going to put ads in the program.

;P

Gotta pay for food somehow... :P

Link to comment
Share on other sites

JrMasterModelBuilder

That's impressive! I see you abandoned your JAM recompiler. If you want, I can give you the source to mine. ActionScript might be a little tricky to convert to C#, but I imagine it C# would run faster.

That would be great, thank you :D

Here ya go! (Note: This is from when I was still putting code on the timeline, so it's probable that I didn't do everything the most efficient way possible.):


//Code written by JrMasterModelBuilder


//File data.

var fileDataArchive:ByteArray = new ByteArray();

//Array of all file names, offsets, and sizes.

var fileListArchive:Array = new Array();

//File build data.

var fileDataBuild:ByteArray = new ByteArray();

//Blank file data.

var blankBytes:ByteArray = new ByteArray();

//List of files to append.

var fileListAppend:Array = new Array();

//List of files to append.

var folderListAppend:Array = new Array();

//Remember folder.

var folderMemory:String = new String();

//Default text.

var defaultText:String = "Click EXTRACT to browse for a JAM file to extract, then select a folder where it will extract all the files.\nClick BUILD to browse for a folder to create an archive from, then select where to save it.";


/////////////////////////////////////////////////////////////

//UI functions.

/////////////////////////////////////////////////////////////

extract_btn.addEventListener(MouseEvent.CLICK, extract_btn_f);

function extract_btn_f(e:MouseEvent):void

{

	erase();

	text_txt.text = defaultText;

	openFile_e();

}

build_btn.addEventListener(MouseEvent.CLICK, build_btn_f);

function build_btn_f(e:MouseEvent):void

{

	erase();

	text_txt.text = "After a folder is selected, archive building will begin. When complete, choose where to save the file.";

	buildMain_b();

}


/////////////////////////////////////////////////////////////

//Useful functions.

/////////////////////////////////////////////////////////////

function erase():void

{

	fileDataArchive.clear();

	fileListArchive = new Array();

	fileDataBuild.clear();

	fileListAppend = new Array();

}

function strRep(string:String, fnd:String, rpl:String):String

{

	return string.split(fnd).join(rpl);

}

function byte(input):int

{

	return input.charCodeAt(0);

}

function pushB(array:Array):void

{

	for(var i:int = 0; i < array.length; i++)

	{

		fileDataBuild.writeByte(array[i]);

	}

}

function noPath(input:String):String

{

	return input.substring(input.lastIndexOf("/") + 1, input.length);

}

function allPath(input:String):String

{

	return input.substring(0, input.lastIndexOf("/") + 1);

}

function nameBytes(input:String):Array

{

	var inputArray:Array = input.split("");

	for(var i:int = 0; i < inputArray.length; i++)

	{

		inputArray[i] = byte(inputArray[i]);

	}

	while(inputArray.length < 12)

	{

		inputArray.push(0);

	}

//	trace(inputArray);

	return inputArray;

}

//Find the integer the 4 bytes at a certain offset represent.

function findNumber(offset:int):int

{

	return fileDataArchive[offset] + (fileDataArchive[offset+1] * 256) + (fileDataArchive[offset+2] * 65536) + (fileDataArchive[offset+3] * 16777216);

}

function replace(where:int, what:int):void

{

//	trace("\treplace(" + where + ", " + what + ");");

	var bytes:Array = findDecimal(what);

	for(var i:int = 0; i < bytes.length; i++)

	{

		fileDataBuild[where+i] = bytes[i];

	}

}

//Decimal to ascii converter.

function char(dec:int):String

{

	return String.fromCharCode(dec);

}

//Number to 4 byte decimal converter.

function findDecimal(input:int):Array

{

	var iplace1:int = 0;

	var iplace2:int = 0;

	var iplace3:int = 0;

	var iplace4:int = 0;

	var returnArray:Array = new Array();


	//Roll it over to the next place until the end giving what's left to the first number.

	while(input >= 16777216)

	{

		iplace4 += 1;

		input -= 16777216;

	}

	while(input >= 65536)

	{

		iplace3 += 1;

		input -= 65536;

	}

	while(input >= 256)

	{

		iplace2 += 1;

		input -= 256;

	}

	iplace1 = input;


	returnArray.push(iplace1);

	returnArray.push(iplace2);

	returnArray.push(iplace3);

	returnArray.push(iplace4);


	return returnArray;

}


/////////////////////////////////////////////////////////////

//Extractor functions.

/////////////////////////////////////////////////////////////

function openFile_e():void

{

	//File object.

	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

	}


	//File type filter object.

	var fileFilter:FileFilter = new FileFilter("All files", "*.*");

	//Open the file dialog with filter and wait for one to be chosen.

    file.browseForOpen("Open", [fileFilter]);

    file.addEventListener(Event.SELECT, fileSelected);

	//When selected.

	function fileSelected(e):void

	{

		folderMemory = allPath(strRep(e.target.nativePath, "\\", "/"));

		//New file stream object.

	    var stream:FileStream = new FileStream();

		//Open the file selected, load all the bytes, and close it back up.

	    stream.open(e.target, FileMode.READ);

		stream.readBytes(fileDataArchive);

		stream.close();

		//Process the file.

		process_e();

		if(fileDataArchive.length < 1)

		{

			return;

		}

		//Save the files.

		text_txt.text = "After a folder is selected, extraction will begin. Please be patient while the files are extracted. When complete, this message will change."

		saveExtract_e();

	}

}

function saveFiles_e(extractto:String):void

{

	for(var i:int = 0; i < fileListArchive.length; i++)

	{

		for(var i2:int = 0; i2 < fileListArchive[i][0].length; i2++)

		{

//			trace(extractto + fileListArchive[i][0][i2] + " | " + fileListArchive[i][1][i2] + " | " + fileListArchive[i][2][i2])

			var fileOpen:File = new File(extractto + fileListArchive[i][0][i2]);

			var stream:FileStream = new FileStream();

			stream.open(fileOpen, FileMode.WRITE);

			//If the file is blank, write blank bytes. Else, write the correct bytes.

			if(fileListArchive[i][1][i2] == 0 || fileListArchive[i][2][i2] == 0)

			{

//				trace(extractto + fileListArchive[i][0][i2] + " BLANK")

				stream.writeBytes(blankBytes);				

			}

			else

			{

				stream.writeBytes(fileDataArchive, fileListArchive[i][1][i2], fileListArchive[i][2][i2]);

			}

			stream.close();

		}

	}

	text_txt.text = "File extraction complete!";

}

function saveExtract_e():void

{

	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

	}

	file.addEventListener(Event.SELECT, dirSelect);

	file.browseForDirectory("Choose a folder.");

	function dirSelect(e:Event):void

	{

		folderMemory = strRep(e.target.nativePath, "\\", "/");

		saveFiles_e(strRep(e.target.nativePath, "\\", "/"));

	}

}

function listFolders_e(offset:int, number:int, folderPath:String):Array

{

	//2D array with folder name and size.

	var folderList:Array = new Array(2);

	folderList[0] = new Array(number);

	folderList[1] = new Array(number);


	for(var i:int = 0; i < number; i++)

	{

		var folderName:String = "";

		for(var i2:int = 0; i2 < 12; i2++)

		{

			if(fileDataArchive[offset+i2] != 0)

			{

				folderName += char(fileDataArchive[offset+i2]);

			}

		}

		//Add above to array.

		folderList[0][i] = folderPath + folderName;

		folderList[1][i] = findNumber(offset + 12);

		//Skip to next file offset.

		offset = offset + 16;

	}

	return folderList;

}

function listFiles_e(offset:int, number:int, folderPath:String):Array

{

	//2D array with file name, offset, and size.

	var fileList:Array = new Array(3);

	fileList[0] = new Array(number);

	fileList[1] = new Array(number);

	fileList[2] = new Array(number);


	for(var i:int = 0; i < number; i++)

	{

		var fileName:String = "";

		for(var i2:int = 0; i2 < 12; i2++)

		{

			if(fileDataArchive[offset+i2] != 0)

			{

				fileName += char(fileDataArchive[offset+i2]);

			}

		}

		//Add above to array.

		fileList[0][i] = folderPath + "/" + fileName;

		fileList[1][i] = findNumber(offset + 12);

		fileList[2][i] = findNumber(offset + 16);

		//Skip to next file offset.

		offset = offset + 20;

	}

	return fileList;

}

function process_e():void

{

	//Check if file has LJAM for a header.

	if(fileDataArchive[0] != 76 && fileDataArchive[1] != 74 && fileDataArchive[2] != 65 && fileDataArchive[3] != 77)

	{

//		trace("ERROR: Not a JAM file.");

		text_txt.text = "ERROR: Not a JAM file.";

		erase();

		return;

	}

	//Start with a folder named "" at 4 dec offset.

	var folderList:Array = new Array(2);

	folderList[0] = new Array(1);

	folderList[1] = new Array(1);

	//Add above to array.

	folderList[0][0] = "";

	folderList[1][0] = 4;

	recurse_e(folderList);

}

function recurse_e(folders:Array):void

{

	for(var i:int; i < folders[0].length; i++)

	{

		if(findNumber(folders[1][i]) == 0)

		{

			//That place has only folders, run this function again for the folders in that folder.

			recurse_e(listFolders_e(folders[1][i]+8, findNumber(folders[1][i]+4), folders[0][i]+"/"));

		}

		if(findNumber(folders[1][i]) > 0)

		{

			//That place has files.

			var fileList:Array = listFiles_e(folders[1][i]+4, findNumber(folders[1][i]), folders[0][i]);

			fileListArchive.push(fileList);

			//This place may also have folders.

			//Find place where folder count exists after file list ends.

			var folderCountPos:int = findNumber(folders[1][i]) * 20 + folders[1][i] + 4;

			//If there are folders there, recurse for them.

			if(findNumber(folderCountPos) > 0)

			{

//				trace(folders[0][i] + " -offset- "+ folderCountPos);

//				trace(listFolders_e(folderCountPos + 4, findNumber(folderCountPos), folders[0][i]+"/"));

				recurse_e(listFolders_e(folderCountPos + 4, findNumber(folderCountPos), folders[0][i]+"/"));

			}

		}

	}

}


/////////////////////////////////////////////////////////////

//Builder functions.

/////////////////////////////////////////////////////////////

function buildMain_b():void

{

	//Put 3 paralell arrays for file name, position pointer, and size pointer.

	fileListAppend.push(new Array());

	fileListAppend.push(new Array());

	fileListAppend.push(new Array());

	//Put 2 paralell arrays for folder name and position pointer.

	folderListAppend.push(new Array());

	folderListAppend.push(new Array());


	//LJAM header.

	pushB([76, 74, 65, 77]);


	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

	}


	file.addEventListener(Event.SELECT, dirSelect);

	file.browseForDirectory("Choose a folder.");

	function dirSelect(e:Event):void

	{

		folderMemory = strRep(e.target.nativePath, "\\", "/");

		recurse_b(strRep(e.target.nativePath, "\\", "/"));

		appendFiles_b();

		saveArchive_b();

	}

}

function infolderListAppend_b(path:String):void

{

//	trace("\t\t\t\t" + folderListAppend[0].length);

	for(var i:int = 0; i < folderListAppend[0].length; i++)

	{

//		trace("\t\tIN STRING: " + folderListAppend[0][i])

		if(path == folderListAppend[0][i])

		{

//			trace("\tFOUND: " + path + " | " + fileDataBuild.length);

			replace(folderListAppend[1][i], fileDataBuild.length);

			//Remove it from the list.

			folderListAppend[0].splice(i, 1);

			folderListAppend[1].splice(i, 1);

			//Break off the loop.

			break;

		}

	}

}

function recurse_b(path:String):void

{

//	trace("path: " + path);

	//Check if the path has been listed.

	infolderListAppend_b(path);

	var file:File = new File(path);

	var fileList:Array = file.getDirectoryListing();


	var fileListDir:Array = new Array();

	var fileListFile:Array = new Array();


	//Process into 2 arrays.

	for(var i:int = 0; i < fileList.length; i++)

	{

		if(fileList[i].isDirectory)

		{

			fileListDir.push(strRep(fileList[i].nativePath, "\\", "/"));

		}

		else

		{

			fileListFile.push(strRep(fileList[i].nativePath, "\\", "/"));

		}

	}

	//Go through files.

	if(fileListFile.length > 0)

	{

		pushB(findDecimal(fileListFile.length));

		for(var iF:int = 0; iF < fileListFile.length; iF++)

		{

//			trace(fileListFile[iF]);

			pushB(nameBytes(noPath(fileListFile[iF])));

			//Remember file name and position for latter.

			fileListAppend[0].push(fileListFile[iF]);

			fileListAppend[1].push(fileDataBuild.length);			

			pushB([0, 0, 0, 0]);

			//And size.

			fileListAppend[2].push(fileDataBuild.length);

			pushB([0, 0, 0, 0]);

		}

		//trace(fileListFile);

	}

	else//No files.

	{

		pushB([0, 0, 0, 0]);

	}

	//Go through folders.

	if(fileListDir.length > 0)

	{

		pushB(findDecimal(fileListDir.length));

		for(var iD:int = 0; iD < fileListDir.length; iD++)

		{

//			trace(fileListDir[iD]);

			pushB(nameBytes(noPath(fileListDir[iD])));

			//Remember folder name and position for latter.

			folderListAppend[0].push(fileListDir[iD]);

//			trace("remember: " + fileListDir[iD]);

			folderListAppend[1].push(fileDataBuild.length);			

			pushB([0, 0, 0, 0]);

		}

		for(iD = 0; iD < fileListDir.length; iD++)

		{

			//For every folder call this function again.

			recurse_b(fileListDir[iD]);

		}

		//trace(fileListDir);

	}

	else//No folders.

	{

		pushB([0, 0, 0, 0]);

	}


//	trace(fileListAppend);

//	trace(folderListAppend);

}

function appendFiles_b():void

{

//	trace("Add the files:");

	for(var i:int = 0; i < fileListAppend[0].length; i++)

	{

		//Edit pointer to this file.

		replace(fileListAppend[1][i], fileDataBuild.length);


		//Open the file.

		var fileIn:ByteArray = new ByteArray();

		//File object.

		var file:File = new File(fileListAppend[0][i]);

		//New file stream object.

	    var stream:FileStream = new FileStream();

		//Open the file selected, load all the bytes, and close it back up.

	    stream.open(file, FileMode.READ);

		stream.readBytes(fileIn);

		stream.close();

		//Append the file.

		fileDataBuild.writeBytes(fileIn);


		//Edit pointer for this file size.

		replace(fileListAppend[2][i], fileIn.length);

	}

//	trace(fileListAppend);

}

function saveArchive_b():void

{

	//File object.

	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

		file = file.parent;

	}

	//Open the file dialog with filter and wait for one to be chosen.

    file.browseForSave("Save");

    file.addEventListener(Event.SELECT, fileSelected);

	//When selected.

	function fileSelected(e):void

	{

		folderMemory = allPath(strRep(e.target.nativePath, "\\", "/"));

		var stream:FileStream = new FileStream();

		stream.open(e.target, FileMode.WRITE);

		stream.writeBytes(fileDataBuild);

		stream.close();

		text_txt.text = defaultText;

	}

}

text_txt.text = defaultText;

Basically what I did was recurse through the folders, creating the file index while remembering the file paths and where their offset and size data would need to be stored later when I append all the files to the JAM file.

Link to comment
Share on other sites

  • 10 months later...

That's impressive! I see you abandoned your JAM recompiler. If you want, I can give you the source to mine. ActionScript might be a little tricky to convert to C#, but I imagine it C# would run faster.

That would be great, thank you :D

Here ya go! (Note: This is from when I was still putting code on the timeline, so it's probable that I didn't do everything the most efficient way possible.):


//Code written by JrMasterModelBuilder


//File data.

var fileDataArchive:ByteArray = new ByteArray();

//Array of all file names, offsets, and sizes.

var fileListArchive:Array = new Array();

//File build data.

var fileDataBuild:ByteArray = new ByteArray();

//Blank file data.

var blankBytes:ByteArray = new ByteArray();

//List of files to append.

var fileListAppend:Array = new Array();

//List of files to append.

var folderListAppend:Array = new Array();

//Remember folder.

var folderMemory:String = new String();

//Default text.

var defaultText:String = "Click EXTRACT to browse for a JAM file to extract, then select a folder where it will extract all the files.\nClick BUILD to browse for a folder to create an archive from, then select where to save it.";


/////////////////////////////////////////////////////////////

//UI functions.

/////////////////////////////////////////////////////////////

extract_btn.addEventListener(MouseEvent.CLICK, extract_btn_f);

function extract_btn_f(e:MouseEvent):void

{

	erase();

	text_txt.text = defaultText;

	openFile_e();

}

build_btn.addEventListener(MouseEvent.CLICK, build_btn_f);

function build_btn_f(e:MouseEvent):void

{

	erase();

	text_txt.text = "After a folder is selected, archive building will begin. When complete, choose where to save the file.";

	buildMain_b();

}


/////////////////////////////////////////////////////////////

//Useful functions.

/////////////////////////////////////////////////////////////

function erase():void

{

	fileDataArchive.clear();

	fileListArchive = new Array();

	fileDataBuild.clear();

	fileListAppend = new Array();

}

function strRep(string:String, fnd:String, rpl:String):String

{

	return string.split(fnd).join(rpl);

}

function byte(input):int

{

	return input.charCodeAt(0);

}

function pushB(array:Array):void

{

	for(var i:int = 0; i < array.length; i++)

	{

		fileDataBuild.writeByte(array[i]);

	}

}

function noPath(input:String):String

{

	return input.substring(input.lastIndexOf("/") + 1, input.length);

}

function allPath(input:String):String

{

	return input.substring(0, input.lastIndexOf("/") + 1);

}

function nameBytes(input:String):Array

{

	var inputArray:Array = input.split("");

	for(var i:int = 0; i < inputArray.length; i++)

	{

		inputArray[i] = byte(inputArray[i]);

	}

	while(inputArray.length < 12)

	{

		inputArray.push(0);

	}

//	trace(inputArray);

	return inputArray;

}

//Find the integer the 4 bytes at a certain offset represent.

function findNumber(offset:int):int

{

	return fileDataArchive[offset] + (fileDataArchive[offset+1] * 256) + (fileDataArchive[offset+2] * 65536) + (fileDataArchive[offset+3] * 16777216);

}

function replace(where:int, what:int):void

{

//	trace("\treplace(" + where + ", " + what + ");");

	var bytes:Array = findDecimal(what);

	for(var i:int = 0; i < bytes.length; i++)

	{

		fileDataBuild[where+i] = bytes[i];

	}

}

//Decimal to ascii converter.

function char(dec:int):String

{

	return String.fromCharCode(dec);

}

//Number to 4 byte decimal converter.

function findDecimal(input:int):Array

{

	var iplace1:int = 0;

	var iplace2:int = 0;

	var iplace3:int = 0;

	var iplace4:int = 0;

	var returnArray:Array = new Array();


	//Roll it over to the next place until the end giving what's left to the first number.

	while(input >= 16777216)

	{

		iplace4 += 1;

		input -= 16777216;

	}

	while(input >= 65536)

	{

		iplace3 += 1;

		input -= 65536;

	}

	while(input >= 256)

	{

		iplace2 += 1;

		input -= 256;

	}

	iplace1 = input;


	returnArray.push(iplace1);

	returnArray.push(iplace2);

	returnArray.push(iplace3);

	returnArray.push(iplace4);


	return returnArray;

}


/////////////////////////////////////////////////////////////

//Extractor functions.

/////////////////////////////////////////////////////////////

function openFile_e():void

{

	//File object.

	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

	}


	//File type filter object.

	var fileFilter:FileFilter = new FileFilter("All files", "*.*");

	//Open the file dialog with filter and wait for one to be chosen.

file.browseForOpen("Open", [fileFilter]);

file.addEventListener(Event.SELECT, fileSelected);

	//When selected.

	function fileSelected(e):void

	{

		folderMemory = allPath(strRep(e.target.nativePath, "\\", "/"));

		//New file stream object.

	 var stream:FileStream = new FileStream();

		//Open the file selected, load all the bytes, and close it back up.

	 stream.open(e.target, FileMode.READ);

		stream.readBytes(fileDataArchive);

		stream.close();

		//Process the file.

		process_e();

		if(fileDataArchive.length < 1)

		{

			return;

		}

		//Save the files.

		text_txt.text = "After a folder is selected, extraction will begin. Please be patient while the files are extracted. When complete, this message will change."

		saveExtract_e();

	}

}

function saveFiles_e(extractto:String):void

{

	for(var i:int = 0; i < fileListArchive.length; i++)

	{

		for(var i2:int = 0; i2 < fileListArchive[i][0].length; i2++)

		{

//			trace(extractto + fileListArchive[i][0][i2] + " | " + fileListArchive[i][1][i2] + " | " + fileListArchive[i][2][i2])

			var fileOpen:File = new File(extractto + fileListArchive[i][0][i2]);

			var stream:FileStream = new FileStream();

			stream.open(fileOpen, FileMode.WRITE);

			//If the file is blank, write blank bytes. Else, write the correct bytes.

			if(fileListArchive[i][1][i2] == 0 || fileListArchive[i][2][i2] == 0)

			{

//				trace(extractto + fileListArchive[i][0][i2] + " BLANK")

				stream.writeBytes(blankBytes);				

			}

			else

			{

				stream.writeBytes(fileDataArchive, fileListArchive[i][1][i2], fileListArchive[i][2][i2]);

			}

			stream.close();

		}

	}

	text_txt.text = "File extraction complete!";

}

function saveExtract_e():void

{

	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

	}

	file.addEventListener(Event.SELECT, dirSelect);

	file.browseForDirectory("Choose a folder.");

	function dirSelect(e:Event):void

	{

		folderMemory = strRep(e.target.nativePath, "\\", "/");

		saveFiles_e(strRep(e.target.nativePath, "\\", "/"));

	}

}

function listFolders_e(offset:int, number:int, folderPath:String):Array

{

	//2D array with folder name and size.

	var folderList:Array = new Array(2);

	folderList[0] = new Array(number);

	folderList[1] = new Array(number);


	for(var i:int = 0; i < number; i++)

	{

		var folderName:String = "";

		for(var i2:int = 0; i2 < 12; i2++)

		{

			if(fileDataArchive[offset+i2] != 0)

			{

				folderName += char(fileDataArchive[offset+i2]);

			}

		}

		//Add above to array.

		folderList[0][i] = folderPath + folderName;

		folderList[1][i] = findNumber(offset + 12);

		//Skip to next file offset.

		offset = offset + 16;

	}

	return folderList;

}

function listFiles_e(offset:int, number:int, folderPath:String):Array

{

	//2D array with file name, offset, and size.

	var fileList:Array = new Array(3);

	fileList[0] = new Array(number);

	fileList[1] = new Array(number);

	fileList[2] = new Array(number);


	for(var i:int = 0; i < number; i++)

	{

		var fileName:String = "";

		for(var i2:int = 0; i2 < 12; i2++)

		{

			if(fileDataArchive[offset+i2] != 0)

			{

				fileName += char(fileDataArchive[offset+i2]);

			}

		}

		//Add above to array.

		fileList[0][i] = folderPath + "/" + fileName;

		fileList[1][i] = findNumber(offset + 12);

		fileList[2][i] = findNumber(offset + 16);

		//Skip to next file offset.

		offset = offset + 20;

	}

	return fileList;

}

function process_e():void

{

	//Check if file has LJAM for a header.

	if(fileDataArchive[0] != 76 && fileDataArchive[1] != 74 && fileDataArchive[2] != 65 && fileDataArchive[3] != 77)

	{

//		trace("ERROR: Not a JAM file.");

		text_txt.text = "ERROR: Not a JAM file.";

		erase();

		return;

	}

	//Start with a folder named "" at 4 dec offset.

	var folderList:Array = new Array(2);

	folderList[0] = new Array(1);

	folderList[1] = new Array(1);

	//Add above to array.

	folderList[0][0] = "";

	folderList[1][0] = 4;

	recurse_e(folderList);

}

function recurse_e(folders:Array):void

{

	for(var i:int; i < folders[0].length; i++)

	{

		if(findNumber(folders[1][i]) == 0)

		{

			//That place has only folders, run this function again for the folders in that folder.

			recurse_e(listFolders_e(folders[1][i]+8, findNumber(folders[1][i]+4), folders[0][i]+"/"));

		}

		if(findNumber(folders[1][i]) > 0)

		{

			//That place has files.

			var fileList:Array = listFiles_e(folders[1][i]+4, findNumber(folders[1][i]), folders[0][i]);

			fileListArchive.push(fileList);

			//This place may also have folders.

			//Find place where folder count exists after file list ends.

			var folderCountPos:int = findNumber(folders[1][i]) * 20 + folders[1][i] + 4;

			//If there are folders there, recurse for them.

			if(findNumber(folderCountPos) > 0)

			{

//				trace(folders[0][i] + " -offset- "+ folderCountPos);

//				trace(listFolders_e(folderCountPos + 4, findNumber(folderCountPos), folders[0][i]+"/"));

				recurse_e(listFolders_e(folderCountPos + 4, findNumber(folderCountPos), folders[0][i]+"/"));

			}

		}

	}

}


/////////////////////////////////////////////////////////////

//Builder functions.

/////////////////////////////////////////////////////////////

function buildMain_b():void

{

	//Put 3 paralell arrays for file name, position pointer, and size pointer.

	fileListAppend.push(new Array());

	fileListAppend.push(new Array());

	fileListAppend.push(new Array());

	//Put 2 paralell arrays for folder name and position pointer.

	folderListAppend.push(new Array());

	folderListAppend.push(new Array());


	//LJAM header.

	pushB([76, 74, 65, 77]);


	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

	}


	file.addEventListener(Event.SELECT, dirSelect);

	file.browseForDirectory("Choose a folder.");

	function dirSelect(e:Event):void

	{

		folderMemory = strRep(e.target.nativePath, "\\", "/");

		recurse_b(strRep(e.target.nativePath, "\\", "/"));

		appendFiles_b();

		saveArchive_b();

	}

}

function infolderListAppend_b(path:String):void

{

//	trace("\t\t\t\t" + folderListAppend[0].length);

	for(var i:int = 0; i < folderListAppend[0].length; i++)

	{

//		trace("\t\tIN STRING: " + folderListAppend[0][i])

		if(path == folderListAppend[0][i])

		{

//			trace("\tFOUND: " + path + " | " + fileDataBuild.length);

			replace(folderListAppend[1][i], fileDataBuild.length);

			//Remove it from the list.

			folderListAppend[0].splice(i, 1);

			folderListAppend[1].splice(i, 1);

			//Break off the loop.

			break;

		}

	}

}

function recurse_b(path:String):void

{

//	trace("path: " + path);

	//Check if the path has been listed.

	infolderListAppend_b(path);

	var file:File = new File(path);

	var fileList:Array = file.getDirectoryListing();


	var fileListDir:Array = new Array();

	var fileListFile:Array = new Array();


	//Process into 2 arrays.

	for(var i:int = 0; i < fileList.length; i++)

	{

		if(fileList[i].isDirectory)

		{

			fileListDir.push(strRep(fileList[i].nativePath, "\\", "/"));

		}

		else

		{

			fileListFile.push(strRep(fileList[i].nativePath, "\\", "/"));

		}

	}

	//Go through files.

	if(fileListFile.length > 0)

	{

		pushB(findDecimal(fileListFile.length));

		for(var iF:int = 0; iF < fileListFile.length; iF++)

		{

//			trace(fileListFile[iF]);

			pushB(nameBytes(noPath(fileListFile[iF])));

			//Remember file name and position for latter.

			fileListAppend[0].push(fileListFile[iF]);

			fileListAppend[1].push(fileDataBuild.length);			

			pushB([0, 0, 0, 0]);

			//And size.

			fileListAppend[2].push(fileDataBuild.length);

			pushB([0, 0, 0, 0]);

		}

		//trace(fileListFile);

	}

	else//No files.

	{

		pushB([0, 0, 0, 0]);

	}

	//Go through folders.

	if(fileListDir.length > 0)

	{

		pushB(findDecimal(fileListDir.length));

		for(var iD:int = 0; iD < fileListDir.length; iD++)

		{

//			trace(fileListDir[iD]);

			pushB(nameBytes(noPath(fileListDir[iD])));

			//Remember folder name and position for latter.

			folderListAppend[0].push(fileListDir[iD]);

//			trace("remember: " + fileListDir[iD]);

			folderListAppend[1].push(fileDataBuild.length);			

			pushB([0, 0, 0, 0]);

		}

		for(iD = 0; iD < fileListDir.length; iD++)

		{

			//For every folder call this function again.

			recurse_b(fileListDir[iD]);

		}

		//trace(fileListDir);

	}

	else//No folders.

	{

		pushB([0, 0, 0, 0]);

	}


//	trace(fileListAppend);

//	trace(folderListAppend);

}

function appendFiles_b():void

{

//	trace("Add the files:");

	for(var i:int = 0; i < fileListAppend[0].length; i++)

	{

		//Edit pointer to this file.

		replace(fileListAppend[1][i], fileDataBuild.length);


		//Open the file.

		var fileIn:ByteArray = new ByteArray();

		//File object.

		var file:File = new File(fileListAppend[0][i]);

		//New file stream object.

	 var stream:FileStream = new FileStream();

		//Open the file selected, load all the bytes, and close it back up.

	 stream.open(file, FileMode.READ);

		stream.readBytes(fileIn);

		stream.close();

		//Append the file.

		fileDataBuild.writeBytes(fileIn);


		//Edit pointer for this file size.

		replace(fileListAppend[2][i], fileIn.length);

	}

//	trace(fileListAppend);

}

function saveArchive_b():void

{

	//File object.

	var file:File = new File();

	if(folderMemory.toString() != "")

	{

		file.nativePath = folderMemory;

		file = file.parent;

	}

	//Open the file dialog with filter and wait for one to be chosen.

file.browseForSave("Save");

file.addEventListener(Event.SELECT, fileSelected);

	//When selected.

	function fileSelected(e):void

	{

		folderMemory = allPath(strRep(e.target.nativePath, "\\", "/"));

		var stream:FileStream = new FileStream();

		stream.open(e.target, FileMode.WRITE);

		stream.writeBytes(fileDataBuild);

		stream.close();

		text_txt.text = defaultText;

	}

}

text_txt.text = defaultText;

Basically what I did was recurse through the folders, creating the file index while remembering the file paths and where their offset and size data would need to be stored later when I append all the files to the JAM file.

Holy crap, that's like gibberish. I only just got into java in my class, though.

Link to comment
Share on other sites

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.