WSH:JavaScriptでADODB.Streamなしにバイナリー出力する

気が向いたので、ADODB.Streamが使用できない環境で、バイナリー出力する方法を考えてみた。
以下の制約がのめるなら、なんとかなる。

  • 先頭2Byteは、ASCII範囲内(0〜128)
  • 全体は偶数バイトであること、もしくは、奇数バイトの場合偶数位置にASCII範囲内がある
  • Scripting.FileSystemObjectは使える


なんかややこしい制約ですが、Windowsのexeファイルやzipファイルであれば問題ないかと。



スクリプト

/*
 * LimitedBinaryStream.js,v 0.1 2014/07/28 june
 *
 * GNU General Public License, version 2 (GPL-2.0), or (at your option) any later version.
 *   http://opensource.org/licenses/GPL-2.0
 */

(function(ctx){
if(!ctx.LimitedBinaryStream) ctx.LimitedBinaryStream = function() {

	this.Log = function(msg) {
		//WScript.Echo((new Date()).toLocaleString() + ' ' + msg);
	}

	this.Open = function() {
		if(arguments.length > 0) this._path = arguments[0];
		if(arguments.length > 1) this._mode = arguments[1];
		this.Log('Open start Path:' + this._path + ' Mode:' + this._mode);
		this._fso = new ActiveXObject('Scripting.FileSystemObject');
		this.LoadFromFile(this._path);
		this.Log('Open end');
	}

	this.LoadFromFile = function(path) {
		this.Log('LoadFromFile start');
		this._path = path;
		var mode = this._mode;
		if(this._mode == this.ModeWrite) mode = 8;
		var type = this.TristateTrue;
		if(arguments.length > 1) type = arguments[1];
		this.Log('LoadFromFile args:' + type + ' ' + mode);
		this._size = 0;
		//this.Size = 0;
		this._position = this._size;
		//this.Position = this.Size;
		if(this._fso.FileExists(path)) {
			var f = this._fso.GetFile(path);
			this._size = f.Size;
			//this.Size = f.Size;
			f = null;
		}
		if(mode == 8) {
			this._position = this._size;
			//this.Position = this.Size;
		}
		this.EOS = (this._position >= this._size);
		this.Log('LoadFromFile pos:' + this._position);
		if(this._stream) {
			this._stream.Close();
		}
		this._stream = null;
		var create = (mode == 8);
		if(this._path && this._path.length && (this._fso.FileExists(this._path) || (create && type == this.TristateFalse))) {
			this._stream = this._fso.OpenTextFile(path, mode, create, type);
		}
		this._partRead = null;
		this._partWrite = null;
		this.Log('LoadFromFile end');
	}

	this.Read = function(num) {
		this.Log('Read start ' + this._position);
		var n = Math.floor(num / 2), p = (num & 1);
		var ret = Array(), i = 0, b;
		if(this._partRead != null && num > 0) {
			ret[i++] = this._partRead;
			this._partRead = null;
			if(p) {
				p = false;
			} else {
				p = true;
				n--;
			}
		}
		for(var j = 0; j < n; j++) {
			b = this._stream.Read(1);
			b = b.charCodeAt(0);
			ret[i++] = b % 256;
			ret[i++] = Math.floor(b / 256);
		}
		if(p) {
			b = this._stream.Read(1);
			b = b.charCodeAt(0);
			ret[i++] = b % 256;
			this._partRead = Math.floor(b / 256);
		}
		this._position += i;
		this.EOS = (this._position >= this._size);
		this.Log('Read end');
		return ret;
	}

	this.Write = function(da) {
		this.Log('Write start ' + this._position);
		var ret = false, i = 0;

		if(this._partWrite != null) {
			da.unshift(this._partWrite);
			this._partWrite = null;
		}
		if(this._position < 2) {
			this.LoadFromFile(this._path, this.TristateFalse);
		}
		while(this._position < 2 && i < da.length) {
			this._stream.Write(String.fromCharCode(da[i++]));
			this._position++;
		}
		if(i < da.length) {
			this.LoadFromFile(this._path, this.TristateTrue);
		}
		var p = (da.length - i) & 1;
		while(i < da.length - 1) {
			if(p && da[i] < 128) {
				this.LoadFromFile(this._path, this.TristateFalse);
				this._stream.Write(String.fromCharCode(da[i++]));
				this.LoadFromFile(this._path, this.TristateTrue);
				this._position++;
				p = false;
			} else {
				this._stream.Write(String.fromCharCode(da[i++] + da[i++] * 256));
				this._position += 2;
			}
		}
		this.Log('Write ' + i + " " + da.length);
		if(i < da.length) {
			this._partWrite = da[i++];
		}
		ret = (i == da.length);

		this.Log('Write end');
		return ret;
	}

	this.Close = function() {
		this.Log('Close start');
		if(this._stream) {
			this._stream.Close();
			this._stream = null;
		}
		this._mode = this.ModeUnknown;
		this._path = null;
		this._fso = null;
		this.Log('Close end');
	}

	this.ModeUnknown = 0;
	this.ModeRead = 1;
	this.ModeWrite = 2;

	this.TristateTrue = -1;
	this.TristateFalse = 0;
}


// Test
function lbsTest() {
	var ret = 0;
	var lbs = new ctx.LimitedBinaryStream();
	lbs.Log(WScript.ScriptName + ' test start');

	lbs.Open('C:\\windows\\system32\\calc.exe', lbs.ModeRead);
	lbs.Log(lbs._size + ' ' + lbs.Read(16));
	lbs.Close();

	lbs.Open('C:\\windows\\system32\\calc.exe', lbs.ModeRead);
	var lbsCopy = new ctx.LimitedBinaryStream();
	lbsCopy.Open('E:\\calc.exe', lbs.ModeWrite);
	lbsCopy.Write(lbs.Read(lbs._size));
	lbs.Close();
	lbsCopy.Close();

	lbs.Log(WScript.ScriptName + ' test fin ' + ret);
}
//lbsTest();

})(this);

ライセンス表記は出来心。
問題があれば言ってくれれば変更します。

使い方

lbsTest関数を見てもらえれば、わかると思います。
calc.exeをeドライブにコピーしているわけですが、fc.exeのバイナリチェックで差分はありませんでした!


基本、ADODB.Streamと同じような感じ(SaveToFile関数はありません)。
ReadやWriteで扱うのが、数値のArrayなので、メモリーの無駄遣い。
適当に書いたので、JavaScriptにおけるオブジェクト指向のお作法は考慮してません。
(勉強しなくちゃね、、、)

備考

一か月以上ブログ更新がなかったことに気づいてのやっつけ仕事。
テストが足りてません!


以前検討したものを進めたものになります。
http://d.hatena.ne.jp/junjun777/20080612/wsh_text_to_binary