mirror of
https://github.com/horsicq/Detect-It-Easy.git
synced 2026-06-24 01:54:08 +00:00
db/read: - since File.readBytes is temporarily down, there's now a replacement for it in there; - all affected files also modified db/chunkparsers: - I'm now collecting the chunked file structure parsers into this one cute file. They're supposed to quickly go through some common blocks, gathering the types, data offsets, data sizes (or whatever else would be necessary otherwise) and guessing the correct expected file size and reporting it too
117 lines
No EOL
6.3 KiB
JavaScript
117 lines
No EOL
6.3 KiB
JavaScript
// https://github.com/horsicq/Detect-It-Easy signature file
|
|
// Authors: hypn0 <hypn0@mail.ru>, Kaens (TG@kaens)
|
|
|
|
init("format", "");
|
|
includeScript("read");
|
|
|
|
/* beautify ignore:start */
|
|
function detect() {
|
|
if (Binary.compare("00010000'Standard Jet DB'00")) {
|
|
sName = "Microsoft Access database (.MDB)";
|
|
bDetected = true;
|
|
}
|
|
|
|
function isDBF() {
|
|
// from https://independent-software.com/dbase-dbf-dbt-file-format.html
|
|
// & https://www.clicketyclick.dk/databases/xbase/format/dbf.html
|
|
// & https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
|
|
nv = X.U8(0); v5 = false;
|
|
switch(nv) {
|
|
case 0x02: sv = 'FoxBase 1.0'; break;
|
|
case 0x03: sv = 'FoxBase 2.x / dBASE III, no memo file'; break;
|
|
case 0x04: sv = 'dBASE IV, no memo file'; break;
|
|
case 0x05: sv = 'dBASE V, no memo file'; break;
|
|
case 0x07: sv = "VISUAL OBJECTS v1.x for dBase III, no memo file"; break;
|
|
case 0x30: sv = 'Visual FoxPro (possibly with DBC)'; break;
|
|
case 0x31: sv = 'Visual FoxPro with auto increment'; break;
|
|
case 0x32: sv = 'Visual FoxPro with varchar/varbinary'; break;
|
|
case 0x43: sv = 'dBASE IV SQL Table, no memo file / Flagship .dbv memo var size'; break;
|
|
case 0x64: sv = 'dBASE IV SQL System, no memo file'; break;
|
|
case 0x83: sv = 'FoxBase 2.x / dBASE III+ with memo file'; break;
|
|
case 0x87: sv = 'VisualObjects 1.x with memo file'; break;
|
|
case 0x8B: sv = 'dBASE IV with memo file'; break;
|
|
case 0x8C: sv = 'dBASE V with memo file'; break;
|
|
case 0x8E: sv = 'dBASE IV with SQL table'; break;
|
|
case 0xB3: sv = '.dbv with memo table'; break;
|
|
case 0xCB: sv = 'dBASE IV SQL Table with memo file'; break;
|
|
case 0xE5: sv = 'Clipper SIX with memo file'; break;
|
|
case 0xF5: sv = 'FoxPro 2 with memo file'; break;
|
|
case 0xFB: sv = 'FoxPro 2, no memo file'; break;
|
|
default: return false
|
|
}
|
|
if(nv == 2) {
|
|
recc = X.U16(1); if(!recc) return false; recsz = X.U16(6); if(!recsz) return false;
|
|
hdrsz = 0x209; rectp = 8; enc = 'IBM437';
|
|
} else {
|
|
y = X.U8(1); if(y < 80) y += 2000; else y += 1900; m = X.U8(2); d = X.U8(3);
|
|
if(X.c('000000',1)) { upd = 'never' } else {
|
|
if(!isWithin(y,1970,2100) || !isWithin(m,1,12) || !isWithin(d,1,31)) return false;
|
|
upd = y+'-'+m.padStart(2,'0')+d.padStart(2,'0') }
|
|
recc = X.U32(4); if(!recc) return false;
|
|
hdrsz = X.U16(8); recsz = X.U16(0xA); if(hdrsz < 0x20 || !recsz) return false;
|
|
res0 = X.U24(0xC); res1 = readU8Array(0xF,13); res2 = X.U32(0x1C);
|
|
if([4,0x8C].indexOf(nv) >= 0) {
|
|
rectp = 0x44; enc = 'CP'+X.SA(0x22,3); if(isWithin((t=X.SA(0x25,1)),'0','9')) enc += t;
|
|
if(enc == 'CPKOI8') enc += X.SA(0x26,1); v5 = true
|
|
} else { rectp = 0x20;
|
|
switch(X.U8(0x1D)) {
|
|
case 0x02: enc = 'CP850'; break; //DOS Multilingual
|
|
case 0x03: enc = 'CP1252'; break; //Windows ANSI
|
|
case 0x04: enc = 'CP10000'; break; //aka. 'macintosh', MAC Roman/W.European Mac
|
|
case 0x64: enc = 'CP852'; break; //Eastern-European MS-DOS
|
|
case 0x65: enc = 'CP865'; break; //Nordic MS-DOS
|
|
case 0x66: enc = 'CP866'; break; //Russian MS-DOS
|
|
case 0x67: enc = 'IBM861'; break; //Icelandic MS-DOS
|
|
//case 0x68: enc = 'Kamenicky (Czech) MS-DOS'; break;
|
|
//case 0x69: enc = 'Mazovia (Polish) MS-DOS'; break;
|
|
case 0x6A: enc = 'IBM737'; break; //Greek MS-DOS (aka. 437G)
|
|
case 0x6B: enc = 'IBM857'; break; //Turkish MS-DOS
|
|
case 0x96: enc = 'CP10007'; break; // aka. 'x-mac-cyrillic', Russian Mac
|
|
case 0x97: enc = 'CP10029'; break; // aka. 'x-mac-ce', MAC Latin 2/C.European Mac
|
|
case 0x98: enc = 'CP10006'; break; //aka. 'x-mac-greek', Greek Mac
|
|
case 0xC8: enc = 'CP1250'; break; //C.European Windows
|
|
case 0xC9: enc = 'CP1251'; break; //Russian Windows
|
|
case 0xCA: enc = 'CP1254'; break; //Turkish Windows
|
|
case 0xCB: enc = 'CP1253'; break; //Greek Windows
|
|
case 0x01: default: enc = 'IBM437' //OEM US
|
|
}
|
|
}
|
|
//TODO check res* depending on version
|
|
}
|
|
validC = ['C','N','L','D','M','F','B','G','P','Y','T','I','V','X','@','O','+','0'];
|
|
fldc = 0; totalfldsz = 1 /* we begin with a deletion flag */; p = rectp; old = [0,1];
|
|
while(X.U8(p) != 0xD && p < hdrsz) {
|
|
name = readU8Array(p,v5? 0x1F: 11); ns = charStat(name,1);
|
|
//_log('fld#'+fldc+' @'+Hex(p)+': '+decEncoding(name,CP866)+' (ns=['+ns+'])')
|
|
if(!name[0] || (ns.indexOf('allxsc') < 0 && ns.indexOf('allforeign') < 0)) return false;
|
|
tp = X.SA(v5? p+0x20: p+0xB,1); if(validC.indexOf(tp) < 0) return false;
|
|
if(nv == 2) fldst = X.U16(p+0xD); else if(v5) fldst = 0; else fldst = X.U32(p+0xC);
|
|
fldsz = X.U8(nv == 2 ? p+0xC: v5? p+0x21: p+0x10); if(!fldsz) return false;
|
|
//_log(' sz:'+fldsz+', old: '+old+'; '+old[0]+'+'+Hex(old[1])+' = '+Hex(old[0]+old[1])+' ?? '+Hex(fldst))
|
|
if(old[0] && fldst && old[0] + old[1] != fldst) return false; old = [fldsz,fldst];
|
|
totalfldsz += fldsz;
|
|
decn = X.U8(nv == 2? p+0xF: p+0x11);
|
|
if(tp === 'N' && decn > fldsz) return false;
|
|
fldc++; p += nv == 2? 0x10: v5? 0x30 : 0x20;
|
|
if(charStat(name.slice(0,name.indexOf(0)),1).indexOf('foreign') >= 0
|
|
&& enc == "IBM437") enc = "CP1251"; //pesky Russians?
|
|
}
|
|
//_log('@'+Hex(p)+': '+fldc+' fields total size '+totalfldsz+' vs. '+recsz)
|
|
if(totalfldsz - recsz != 0) return false;
|
|
del = 0; if(X.isDeepScan()) {
|
|
p = hdrsz; for(i=0; i < recc; i++,p+=recc) if(X.U8(p) == 0x2A) del++
|
|
}
|
|
sz = hdrsz+recc*recsz; if(X.U8(sz) == 0x1A) sz++;
|
|
return sz <= X.Sz() || X.isHeuristicScan()
|
|
}
|
|
if(!bDetected && isDBF()) {
|
|
sName = "DBase Database (.DBF)"; sVersion = sv; bDetected = true;
|
|
if(X.isVerbose()) {
|
|
sOption('fld:'+fldc+' rec:'+recc+(del?'(* '+del+')':'')+' enc:'+enc+' sz:'+outSz(sz))
|
|
}
|
|
}
|
|
|
|
|
|
return result()
|
|
}
|
|
/* beautify ignore:end */ |