Skip to content

Instantly share code, notes, and snippets.

@ztdgz
Created October 23, 2012 00:44
Show Gist options
  • Save ztdgz/3935934 to your computer and use it in GitHub Desktop.
Save ztdgz/3935934 to your computer and use it in GitHub Desktop.
Upink game data "decryption" - "redshirt" processor, in JavaScript.
/*******************
I think the drag and drop code in here was mostly copy-pasted
from http://www.html5rocks.com/en/tutorials/file/dndfiles/
I don't know if I changed it, or if I messed around with it...
It may or may not work in your browser, so make changes
and share if you need to.
-Mike
-- what's it do?
So, basically this is an implementation of the "redshirt" "crypto" used
to obfuscate save game data in Uplink by Introversion Software.
You can drag and drop your saved user.dat files onto the page and it should
generate a dataurl link for you to save back to your machine.
Only tested in Chrome... So... yeah. ;-)
You could then load the saved file in a hex editor, change some junk,
then drop the modified file back on the page, it will generate a newly
"redshirted" file the game should be able to load. :-)
-- why's it so ugly...?
I couldn't be bothered... was just messing around. What do you want from me? :-P
Maybe you could fix it :-D
-- today is 23-Oct-2012 ... well, where I live it is.
--jsfiddle.net :
http://jsfiddle.net/4ADpB/1/
*******************/
function checkRedShirt(r) {
r = new Uint8Array(r);
return (r[0]==82 && //R
r[1]==69 && //E
r[2]==68 && //D
r[3]==83 && //S
r[4]==72 && //H
r[5]==82 && //R
r[6]==84 && //T
r[7]==50 && //2
r[8]==0) //Terminate header
}
function redShirt(txt) {
var out, header=9;
if(typeof txt === 'string') {
var t = new ArrayBuffer(txt.length);
var dataView = new DataView(t);
for(var i=0; i<txt.length; i++) dataView.setInt8(i, txt.charCodeAt(i));
txt = t;
}
if(checkRedShirt(txt)) {
out = new ArrayBuffer(txt.byteLength-9);
txt = txt.slice(9);
header = 0;
} else {
out = new ArrayBuffer(txt.byteLength+9);
var abTxt = new DataView(out);
var redshirt = "REDSHRT2";
for(var i=0; i<9; i++) if(i<8) abTxt.setInt8(i, redshirt.charCodeAt(i)); else abTxt.setInt8(i, 0x00);
}
var abTxt = new DataView(out);
var ar = new Uint8Array(txt);
for(var i=header; i<out.byteLength; i++) {
abTxt.setInt8(i, (ar[i-header] + 128) % 256);
}
return out;
}
function _arrayBufferToBase64( buffer ) {
var binary = ''
var bytes = new Uint8Array( buffer )
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] )
}
return window.btoa( binary );
}
function handleDrag(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = "copy";
}
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer.files; // FileList object.
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
f.size, ' bytes, last modified: ',
f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
'</li>');
}
var u = document.createElement('ul');
u.innerHTML = output.join('');
document.body.appendChild(u);
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
reader.onload = (function(theFileName) {
return function(e) {
var r = redShirt(e.target.result);
var d = _arrayBufferToBase64(r);
//var ext = "";
if(checkRedShirt(r)) theFileName+= ".red";
else if(theFileName.substr(theFileName.length-4)==".red") theFileName = theFileName.substr(0,theFileName.length-4);
var link = '<a download="' + theFileName + '" href="data:application/stream;base64,' + d + '">Download ' + theFileName + '...</a>';
var div = document.createElement('div');
div.innerHTML = link;
document.body.appendChild(div);
};
})(f.name);
reader.readAsArrayBuffer(f);
}
}
function init() {
var dropZone = document.createElement('div');
dropZone.style.border = "2px dashed #aaa";
dropZone.style.margin = "10px";
dropZone.style.padding = "30px";
dropZone.style.position = 'absolute';
dropZone.style.left = '0px';
dropZone.style.right = '0px';
dropZone.style.top = '0px';
dropZone.style.height = "50px";
dropZone.style.textAlign = 'center';
dropZone.style.font = '20pt bold,"Arial"';
dropZone.style.color= '#aaa';
dropZone.addEventListener('dragover', handleDrag, false);
dropZone.addEventListener('drop', handleDrop, false);
dropZone.innerText = "Drop files here";
document.body.appendChild(dropZone);
document.body.style.paddingTop = '140px';
}
window.onload = init;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment