how can I read a binary file with in javascript (Cscript.exe)?
Asked Answered
L

7

5

I think I can't read a binary file with the Scripting.FileSystemObject class. Can I do it with ADODB.Stream?

Something else?

I want to get an array of bytes.

thanks.

Lasky answered 28/5, 2010 at 3:35 Comment(0)
L
4

I looked and found Reading and Writing Binary Files using JScript on codeproject.

Not quite what I wanted, but pretty close.

It uses ADODB.Stream to read a local file. It's apparently not possible to read a binary file using Scripting.FileSystemObject in a general way, because it's not possible to set the codepage using fso. fso always uses the ambient codepage, which means results will vary depending on the machine's configuration. ADODB.Stream allows the program to specify the codepage. The next step is to map from the "text" read into a regular hex byte. That's the part that the above article provides.

The result I used with success is below.

// BinaryFileReader.js
// ------------------------------------------------------------------
//
// give the ability to read a binary file into an array of bytes,
// to Javascript.
//
// the mapping is based on code from:
//   http://www.codeproject.com/KB/scripting/Exsead7.aspx
//
// Created    : Fri May 28 05:20:31 2010
// Last-saved : <2010-May-28 06:01:34>
//
// ------------------------------------------------------------------

(function(){

    BinaryFileReader = {};

    var FileReadTypes = {
        adTypeBinary : 1,
        adTypeText   : 2
    };

    var backward = [];
    backward['C7']   = '80';
    backward['FC']   = '81';
    backward['E9']   = '82';
    backward['E2']   = '83';
    backward['E4']   = '84';
    backward['E0']   = '85';
    backward['E5']   = '86';
    backward['E7']   = '87';
    backward['EA']   = '88';
    backward['EB']   = '89';
    backward['E8']   = '8A';
    backward['EF']   = '8B';
    backward['EE']   = '8C';
    backward['EC']   = '8D';
    backward['C4']   = '8E';
    backward['C5']   = '8F';
    backward['C9']   = '90';
    backward['E6']   = '91';
    backward['C6']   = '92';
    backward['F4']   = '93';
    backward['F6']   = '94';
    backward['F2']   = '95';
    backward['FB']   = '96';
    backward['F9']   = '97';
    backward['FF']   = '98';
    backward['D6']   = '99';
    backward['DC']   = '9A';
    backward['A2']   = '9B';
    backward['A3']   = '9C';
    backward['A5']   = '9D';
    backward['20A7'] = '9E';
    backward['192']  = '9F';
    backward['E1']   = 'A0';
    backward['ED']   = 'A1';
    backward['F3']   = 'A2';
    backward['FA']   = 'A3';
    backward['F1']   = 'A4';
    backward['D1']   = 'A5';
    backward['AA']   = 'A6';
    backward['BA']   = 'A7';
    backward['BF']   = 'A8';
    backward['2310'] = 'A9';
    backward['AC']   = 'AA';
    backward['BD']   = 'AB';
    backward['BC']   = 'AC';
    backward['A1']   = 'AD';
    backward['AB']   = 'AE';
    backward['BB']   = 'AF';
    backward['2591'] = 'B0';
    backward['2592'] = 'B1';
    backward['2593'] = 'B2';
    backward['2502'] = 'B3';
    backward['2524'] = 'B4';
    backward['2561'] = 'B5';
    backward['2562'] = 'B6';
    backward['2556'] = 'B7';
    backward['2555'] = 'B8';
    backward['2563'] = 'B9';
    backward['2551'] = 'BA';
    backward['2557'] = 'BB';
    backward['255D'] = 'BC';
    backward['255C'] = 'BD';
    backward['255B'] = 'BE';
    backward['2510'] = 'BF';
    backward['2514'] = 'C0';
    backward['2534'] = 'C1';
    backward['252C'] = 'C2';
    backward['251C'] = 'C3';
    backward['2500'] = 'C4';
    backward['253C'] = 'C5';
    backward['255E'] = 'C6';
    backward['255F'] = 'C7';
    backward['255A'] = 'C8';
    backward['2554'] = 'C9';
    backward['2569'] = 'CA';
    backward['2566'] = 'CB';
    backward['2560'] = 'CC';
    backward['2550'] = 'CD';
    backward['256C'] = 'CE';
    backward['2567'] = 'CF';
    backward['2568'] = 'D0';
    backward['2564'] = 'D1';
    backward['2565'] = 'D2';
    backward['2559'] = 'D3';
    backward['2558'] = 'D4';
    backward['2552'] = 'D5';
    backward['2553'] = 'D6';
    backward['256B'] = 'D7';
    backward['256A'] = 'D8';
    backward['2518'] = 'D9';
    backward['250C'] = 'DA';
    backward['2588'] = 'DB';
    backward['2584'] = 'DC';
    backward['258C'] = 'DD';
    backward['2590'] = 'DE';
    backward['2580'] = 'DF';
    backward['3B1']  = 'E0';
    backward['DF']   = 'E1';
    backward['393']  = 'E2';
    backward['3C0']  = 'E3';
    backward['3A3']  = 'E4';
    backward['3C3']  = 'E5';
    backward['B5']   = 'E6';
    backward['3C4']  = 'E7';
    backward['3A6']  = 'E8';
    backward['398']  = 'E9';
    backward['3A9']  = 'EA';
    backward['3B4']  = 'EB';
    backward['221E'] = 'EC';
    backward['3C6']  = 'ED';
    backward['3B5']  = 'EE';
    backward['2229'] = 'EF';
    backward['2261'] = 'F0';
    backward['B1']   = 'F1';
    backward['2265'] = 'F2';
    backward['2264'] = 'F3';
    backward['2320'] = 'F4';
    backward['2321'] = 'F5';
    backward['F7']   = 'F6';
    backward['2248'] = 'F7';
    backward['B0']   = 'F8';
    backward['2219'] = 'F9';
    backward['B7']   = 'FA';
    backward['221A'] = 'FB';
    backward['207F'] = 'FC';
    backward['B2']   = 'FD';
    backward['25A0'] = 'FE';
    backward['A0']   = 'FF';

    var hD="0123456789ABCDEF";

    var d2h = function(d)
    {
        var h = hD.substr(d&15,1);
        while(d>15) {d>>>=4;h=hD.substr(d&15,1)+h;}
        return h;
    }

    var h2d = function(h)
    {
        return parseInt(h,16);
    }

    var toByteArray = function(inString) {
        var encArray = [];
        var sL = inString.length;
        for (var i=0;i<sL;i++) {
            var cc = inString.charCodeAt(i);
            if(cc>=128) {
                var h = backward[''+d2h(cc)];
                cc = h2d(h);
            }
            encArray.push(cc);
        }
        return encArray;
    }


    var _internalReadAll = function(path) {
        var bs = WScript.CreateObject("ADODB.Stream")
        bs.Type = FileReadTypes.adTypeText;
        bs.CharSet = '437';
        bs.Open();
        bs.LoadFromFile(path);
        var what = bs.ReadText;
        bs.Close();
        return what;
    }

    BinaryFileReader.ReadAllBytes = function(name)
    {
        var string = _internalReadAll(name);
        return toByteArray(string);
    }

})();

And usage is:

    var bytes = BinaryFileReader.ReadAllBytes(filename);
Lasky answered 28/5, 2010 at 10:8 Comment(1)
How does it work in other locales? Your backward translation seems pre-set. Is it possible to costruct the translation at run-time somehow?Slack
L
6

It also works to read the file as a binary, and then use VBScript to transform the object that comes back (an array of variant) into a Javascript array of bytes. To do that you need to mix VBScript and Javascript together using a .wsf file. You still need ADODB.Stream.

<job id="Something">
  <script id="BFRHelper.vbs" language="VBScript">
    Public Function VbBinaryToArray(Binary)
        Dim i
        ReDim byteArray(LenB(Binary))
        For i = 1 To LenB(Binary)
            byteArray(i-1) = AscB(MidB(Binary, i, 1))
        Next
        VbBinaryToArray = byteArray
    End Function
  </script>

  <script language="JavaScript" id="BFR2.js">

    (function(){

        BinaryFileReader = {};

        var FileReadTypes = {
            adTypeBinary : 1,
            adTypeText   : 2
        };

        BinaryFileReader.ReadAllBytes = function(path)
        {
            var bs = WScript.CreateObject("ADODB.Stream");
            bs.Type = FileReadTypes.adTypeBinary;
            bs.Open();
            bs.LoadFromFile(path);
            var what = bs.Read;
            bs.Close();
            var array = VbBinaryToArray(what).toArray();
            // I find the length property is 1 higher than it ought to be
            var aL = array.length;
            array.length = aL -1;
            return array;
        };
    })();

    var content = BinaryFileReader.ReadAllBytes(path); 

  </script>
</job>
Lasky answered 28/5, 2010 at 10:47 Comment(2)
This is the way to go. The only solution both complete and correct.Slack
How could the opposite be done? That is, from a byte array to a variant.Shelves
L
4

I looked and found Reading and Writing Binary Files using JScript on codeproject.

Not quite what I wanted, but pretty close.

It uses ADODB.Stream to read a local file. It's apparently not possible to read a binary file using Scripting.FileSystemObject in a general way, because it's not possible to set the codepage using fso. fso always uses the ambient codepage, which means results will vary depending on the machine's configuration. ADODB.Stream allows the program to specify the codepage. The next step is to map from the "text" read into a regular hex byte. That's the part that the above article provides.

The result I used with success is below.

// BinaryFileReader.js
// ------------------------------------------------------------------
//
// give the ability to read a binary file into an array of bytes,
// to Javascript.
//
// the mapping is based on code from:
//   http://www.codeproject.com/KB/scripting/Exsead7.aspx
//
// Created    : Fri May 28 05:20:31 2010
// Last-saved : <2010-May-28 06:01:34>
//
// ------------------------------------------------------------------

(function(){

    BinaryFileReader = {};

    var FileReadTypes = {
        adTypeBinary : 1,
        adTypeText   : 2
    };

    var backward = [];
    backward['C7']   = '80';
    backward['FC']   = '81';
    backward['E9']   = '82';
    backward['E2']   = '83';
    backward['E4']   = '84';
    backward['E0']   = '85';
    backward['E5']   = '86';
    backward['E7']   = '87';
    backward['EA']   = '88';
    backward['EB']   = '89';
    backward['E8']   = '8A';
    backward['EF']   = '8B';
    backward['EE']   = '8C';
    backward['EC']   = '8D';
    backward['C4']   = '8E';
    backward['C5']   = '8F';
    backward['C9']   = '90';
    backward['E6']   = '91';
    backward['C6']   = '92';
    backward['F4']   = '93';
    backward['F6']   = '94';
    backward['F2']   = '95';
    backward['FB']   = '96';
    backward['F9']   = '97';
    backward['FF']   = '98';
    backward['D6']   = '99';
    backward['DC']   = '9A';
    backward['A2']   = '9B';
    backward['A3']   = '9C';
    backward['A5']   = '9D';
    backward['20A7'] = '9E';
    backward['192']  = '9F';
    backward['E1']   = 'A0';
    backward['ED']   = 'A1';
    backward['F3']   = 'A2';
    backward['FA']   = 'A3';
    backward['F1']   = 'A4';
    backward['D1']   = 'A5';
    backward['AA']   = 'A6';
    backward['BA']   = 'A7';
    backward['BF']   = 'A8';
    backward['2310'] = 'A9';
    backward['AC']   = 'AA';
    backward['BD']   = 'AB';
    backward['BC']   = 'AC';
    backward['A1']   = 'AD';
    backward['AB']   = 'AE';
    backward['BB']   = 'AF';
    backward['2591'] = 'B0';
    backward['2592'] = 'B1';
    backward['2593'] = 'B2';
    backward['2502'] = 'B3';
    backward['2524'] = 'B4';
    backward['2561'] = 'B5';
    backward['2562'] = 'B6';
    backward['2556'] = 'B7';
    backward['2555'] = 'B8';
    backward['2563'] = 'B9';
    backward['2551'] = 'BA';
    backward['2557'] = 'BB';
    backward['255D'] = 'BC';
    backward['255C'] = 'BD';
    backward['255B'] = 'BE';
    backward['2510'] = 'BF';
    backward['2514'] = 'C0';
    backward['2534'] = 'C1';
    backward['252C'] = 'C2';
    backward['251C'] = 'C3';
    backward['2500'] = 'C4';
    backward['253C'] = 'C5';
    backward['255E'] = 'C6';
    backward['255F'] = 'C7';
    backward['255A'] = 'C8';
    backward['2554'] = 'C9';
    backward['2569'] = 'CA';
    backward['2566'] = 'CB';
    backward['2560'] = 'CC';
    backward['2550'] = 'CD';
    backward['256C'] = 'CE';
    backward['2567'] = 'CF';
    backward['2568'] = 'D0';
    backward['2564'] = 'D1';
    backward['2565'] = 'D2';
    backward['2559'] = 'D3';
    backward['2558'] = 'D4';
    backward['2552'] = 'D5';
    backward['2553'] = 'D6';
    backward['256B'] = 'D7';
    backward['256A'] = 'D8';
    backward['2518'] = 'D9';
    backward['250C'] = 'DA';
    backward['2588'] = 'DB';
    backward['2584'] = 'DC';
    backward['258C'] = 'DD';
    backward['2590'] = 'DE';
    backward['2580'] = 'DF';
    backward['3B1']  = 'E0';
    backward['DF']   = 'E1';
    backward['393']  = 'E2';
    backward['3C0']  = 'E3';
    backward['3A3']  = 'E4';
    backward['3C3']  = 'E5';
    backward['B5']   = 'E6';
    backward['3C4']  = 'E7';
    backward['3A6']  = 'E8';
    backward['398']  = 'E9';
    backward['3A9']  = 'EA';
    backward['3B4']  = 'EB';
    backward['221E'] = 'EC';
    backward['3C6']  = 'ED';
    backward['3B5']  = 'EE';
    backward['2229'] = 'EF';
    backward['2261'] = 'F0';
    backward['B1']   = 'F1';
    backward['2265'] = 'F2';
    backward['2264'] = 'F3';
    backward['2320'] = 'F4';
    backward['2321'] = 'F5';
    backward['F7']   = 'F6';
    backward['2248'] = 'F7';
    backward['B0']   = 'F8';
    backward['2219'] = 'F9';
    backward['B7']   = 'FA';
    backward['221A'] = 'FB';
    backward['207F'] = 'FC';
    backward['B2']   = 'FD';
    backward['25A0'] = 'FE';
    backward['A0']   = 'FF';

    var hD="0123456789ABCDEF";

    var d2h = function(d)
    {
        var h = hD.substr(d&15,1);
        while(d>15) {d>>>=4;h=hD.substr(d&15,1)+h;}
        return h;
    }

    var h2d = function(h)
    {
        return parseInt(h,16);
    }

    var toByteArray = function(inString) {
        var encArray = [];
        var sL = inString.length;
        for (var i=0;i<sL;i++) {
            var cc = inString.charCodeAt(i);
            if(cc>=128) {
                var h = backward[''+d2h(cc)];
                cc = h2d(h);
            }
            encArray.push(cc);
        }
        return encArray;
    }


    var _internalReadAll = function(path) {
        var bs = WScript.CreateObject("ADODB.Stream")
        bs.Type = FileReadTypes.adTypeText;
        bs.CharSet = '437';
        bs.Open();
        bs.LoadFromFile(path);
        var what = bs.ReadText;
        bs.Close();
        return what;
    }

    BinaryFileReader.ReadAllBytes = function(name)
    {
        var string = _internalReadAll(name);
        return toByteArray(string);
    }

})();

And usage is:

    var bytes = BinaryFileReader.ReadAllBytes(filename);
Lasky answered 28/5, 2010 at 10:8 Comment(1)
How does it work in other locales? Your backward translation seems pre-set. Is it possible to costruct the translation at run-time somehow?Slack
N
2

If you prefer pure jScript with COM objects, maybe my method could be useful. It creates a jscript string from binary data. I prefer to use COM objects instead code page translations because speed.

//Reads a binary file, returns a string
function binaryFileToString(fileName) {
    var binStream = new ActiveXObject("ADODB.Stream");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var size = (fs.getFile(fileName)).size;

    binStream.Type = 1; //adTypeBinary
    binStream.Open;

    binStream.loadFromFile(fileName);
    var binVariant = binStream.read();
    var adLongVarChar = 201;
    var RS = new ActiveXObject("ADODB.Recordset");

    RS.fields.append("mBinary", adLongVarChar, size);
    RS.open();
    RS.addNew();
    RS("mBinary").appendChunk(binVariant);
    RS.update();
    return RS("mBinary").value;
};
Nutrition answered 5/7, 2011 at 16:36 Comment(1)
This seems to work exactly like FSO File.OpenAsTextStream(1).ReadAll().Slack
I
0
Function ReadByteArray(strFileName)
Const adTypeBinary = 1
Dim bin
    Set bin = CreateObject("ADODB.Stream")
    bin.Type = adTypeBinary
    bin.Open
    bin.LoadFromFile strFileName
    ReadByteArray = bin.Read
End Function

From http://www.ericphelps.com/q193998/index.htm

Infuriate answered 28/5, 2010 at 3:54 Comment(1)
hmm, translating that to Javascript didn't solve the problem. The issue is that the thing obtained from bin.Read is not a Javascript byte array. It's an array of variants. To use that code, it's necessary to map from the variant array to a JS byte array. See my code #2927004Lasky
M
0

Cheeso’s answer works, and yields an array of numbers. An alternative is to replace line 5 with

a.push(ts.Read(1));

This yields an array of single characters instead of an array of numbers. On my system (Windows 7 64-bit), writing the bytes one at a time in a loop is too slow, even though cheeso’s seemingly equivalent reading function is acceptably fast. Writing an array in one Write yields commas separating the bytes. But bytes represented by an array of single characters can be written by the function

function WriteByte (File, Byte) {
    var Text = "";
    for (I = 0; I < Byte.length; I ++) Text += Byte [I];
    File.Write (Text);
}

(in which File is opened for ASCII) which is acceptably fast in my application. (There is a bug in JavaScript on my system that, if a string containing null characters is passed to a function, in the function the .length property of the string is correct but every character after the first null character is null (as distinct from the null character). However, in the here-cited function, null characters in Text are managed correctly as well as expeditiously.)

Another advantage of representing bytes by characters instead of numbers is that each byte is represented by two bytes instead of four. Of course, the disadvantage is that many operations on the bytes require the .charCodeAt (0) method.

Moltke answered 9/9, 2015 at 12:9 Comment(0)
S
0

I revealed that the "WindowsInstaller.Installer" can return true string bytes one by one with certain parameters passed. So you can use it in combination with "ADODB.Stream" to access true bytes from file.

Then you can also write them into new file one by one or in a bunch by using "SAPI.SpFileStream" ActiveX object (in Russian: http://forum.script-coding.com/viewtopic.php?id=10092). It depends on what you need.

All these ActiveX objects work fine this way in pure JScript (extended JavaScript) on any locale including Chinese and it is expected they are built-in in every Windows since XP or 2000 by default.

Here is a sample (you need to save it as a text file with "js" extension or use HTA instead):

var doc = GetObject('\\', 'htmlfile');
doc.write("<xml><_ xmlns:dt='urn:schemas-microsoft-com:datatypes'><_ dt:dt='bin.hex'/></_></xml>");
var xml = doc.documentElement.firstChild.children[1];
xml.preserveWhiteSpace = 1;

var hex = xml.firstChild.childNodes[0],

bytesToHex = function(bytes)          //convert true bytes to hex string
{
    hex.nodeTypedValue = bytes;
    return hex.text;
},

hexToBytes = function(hString)        //convert hex string to true bytes
{
    hex.text = hString;
    return hex.nodeTypedValue;
},

source = new ActiveXObject('WindowsInstaller.Installer').CreateRecord(1),     //source file stream
memory = new ActiveXObject('ADODB.Stream'),                                   //memory stream
output = new ActiveXObject('SAPI.SpFileStream'),                              //output file stream

sourceRead = function(bytesCount)     //read true bytes from source file stream starting from its previous position
{
    memory.Position = 0;
    memory.Type = 2;                                            //adTypeText = 2
    memory.Charset = 'Unicode';
    memory.WriteText(source.ReadStream(1, bytesCount, 3));      //msiReadStreamDirect = 3
    memory.SetEOS();
    memory.Position = 0;
    memory.Type = 1;                                            //adTypeBinary = 1
    memory.Position = 2;                                        //skip "BOM" header
    return memory.Read(bytesCount);
}

source.SetStream(1, 'C:\\Windows\\System32\\shell32.dll');      //open source file stream for reading

memory.Open();                                                  //open memory stream for reading and writing

output.Format.Type = 1;                                         //SAFTText = 1
output.Open('your output path goes here.bin', 3);               //create and open output file for writing (SSFMCreateForWrite = 3)

var arrayOfTrueBytes = [],
arrayOfHexBytes = [],
trueByte;
for(var i = 0; i < 7; i ++)                                     //fill arrays with first 7 bytes of source file stream one by one
{
    trueByte = sourceRead(1);
    arrayOfTrueBytes[i] = trueByte;
    arrayOfHexBytes[i] = bytesToHex(trueByte);
}

doc.parentWindow.alert(arrayOfHexBytes);        //show hex representation of true bytes in array

for(var i = 0; i < 7; i ++)
    output.Write(arrayOfTrueBytes[i]);          //write true bytes one by one from array to output file stream

output.Write(sourceRead(10));                   //write a bunch of next 10 true bytes of source file stream to output file stream
output.Write('\u3231\x20');                     //write string bytes to output file stream
output.Write(hexToBytes('68656c6c6f'));         //write true bytes converted from hex string to output file stream

I used these approaches in my HTML application called Resource Extractor. So if you need complex samples on how to work with binary data you can also check this application at https://tastyscriptsforfree.wixsite.com/page/scripts.

Santinasantini answered 17/10, 2020 at 20:45 Comment(1)
hex conversion samples are based on forum.script-coding.com/viewtopic.php?id=6957 (in Russian).Santinasantini
L
-1

Read the bytes of a file, in jscript.

    function readAllBytes(path) {
        var fso = new ActiveXObject("Scripting.FileSystemObject"),
            ts = fso.OpenTextFile(path, 1), a = [];
        while (!ts.AtEndOfStream)
            a.push(ts.Read(1).charCodeAt(0));

        ts.Close();
        return a;
    }
Lasky answered 23/6, 2012 at 1:58 Comment(2)
how to read binary and convert to hexadecimal when reading? Using your code, not using ADODB.StreamSpoofery
ActiveXObject has been deprecated, only worked in IE. It should NOT be used, even if working in some browsersBrower

© 2022 - 2024 — McMap. All rights reserved.