grf-extractor
Version:
A command line GRF extractor, used to manage Ragnarok's .grf files.
172 lines (138 loc) • 3.83 kB
JavaScript
// Entry point
// Parse arguments and setup the enviroment
// Dependencies
var argv = require('optimist')
var _package = require('./package.json')
var Grf = require('./lib/grf.js')
var ProgressBar = require('progress')
var Fs = require('fs')
// Setup args
argv.options('g', {
alias: 'grf',
describe: 'The grf file to be worked on.',
})
argv.options('s', {
alias: 'search',
describe: 'Search a single file on the .grf or a list of files separated by comma. RegExp are supported.',
})
argv.options('l', {
alias: 'list',
describe: 'List files inside the grf.',
})
argv.options('o', {
alias: 'output',
describe: 'Output directoy to write the extracted files to.',
})
argv.options('e', {
alias: 'extract',
describe: 'Extract a single file from the .grf, prints to stdout. Example: grf-extractor -e data/clientinfo.xml > clientinfo.xml',
})
argv.options('c', {
alias: 'concurrency',
default: 100,
describe: 'Concurrency rate, how many parallel extractions should we do, set it to higher values for a faster extraction.',
})
argv.options('v', {
alias: 'verbose',
describe: 'Enable verbose output, will print debug messages.',
})
argv.options('version', {
describe: 'Print package version.',
})
argv.options('h', {
alias: 'help',
describe: 'Print help and usage information',
})
// Parse arguments
var arg = argv.argv
// Print version
if(arg.version) {
console.log(_package.version)
return
}
// Print help
if(!arg.g || arg.h) {
console.error("Usage: grf-extractor -g data.grf -o output_dir")
argv.showHelp()
return
}
// Init working grf
var fd = Fs.openSync(arg.g, 'r')
var grf = new Grf({
fd: fd
})
// Set debugging mode
if(typeof arg.v !== 'undefined') {
grf.printDebug = true
}
// Search and print information about a single file in the grf
if(typeof arg.s === 'string') {
var list = arg.s.split(',')
for(var i in list) {
var pattern = list[i]
grf.searchPattern(new RegExp(pattern, 'gi'), function(matches) {
console.log("Search for %s:", pattern)
if(! matches) {
console.log("- Nothing found matching this pattern.")
return
}
for(var m in matches) {
var match = matches[m]
console.log("- File path: %s - File size: %d kb", match.filename, match.real_size/1000)
}
})
}
return
}
// List files inside the grf
if(typeof arg.l !== 'undefined') {
var list = grf.entries
for(var i in list) {
var file = list[i]
console.log("%s - size in bytes: %d", file.filename, file.real_size)
}
return
}
// Extract a single file frm the grf
if(typeof arg.e === 'string') {
var filename = arg.e
// We need to replace our normal slashes for windows forward slash
filename = filename.replace( /\//g, '\\')
var exists = grf.getFile(filename, function(data){
var buf = new Buffer( new Uint8Array(data))
process.stdout.write(buf)
})
if(exists === false) {
console.error("File '%s' not in the .grf, don't forget to put the data/ folder in front.", filename)
console.error("Example: -e data/clientinfo.xml")
}
return
}
// Default action, extract the entire grf
var extraction = grf.extract({
output: (typeof arg.o !== 'string' ? '' : arg.o),
concurrency: arg.c,
})
extraction.on('start', function() {
console.log("Extraction has started on %d files.", grf.entries.length)
// Pretty progress bar extraction on process
var bar = new ProgressBar('Progress [:current/:total] [:bar] :percent :elapseds - :etas', {
width: 100,
total: grf.entries.length
})
var last = 0
var timer = setInterval(function () {
var progress = extraction.progress()
var rate = progress - last
last = progress
bar.tick(rate)
if (bar.complete) {
console.log("Finished extracting GRF.")
process.nextTick(process.exit)
}
}, 100)
})
extraction.on('error', function(err) {
console.error("Extraction error", err)
})