// Detect It Easy: detection rule file
// Author: horsicq <horsicq@gmail.com>
// 1995-2007 Borland Delphi
// 2007-2014 Embarcadero Delphi;
// https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Compiler_Versions
// coauthor: sendersu

meta("compiler", "Borland Delphi");

includeScript("Borland");

function getVersion() {
    if (PE.isNet()) {
        sOptions = ".NET";

        if (PE.isNetObjectPresent("Borland.Vcl.Types")) {
            sVersion = "8";
        } else if (PE.isNetObjectPresent("Borland.Eco.Interfaces")) {
            sVersion = "8 Eco WinForm";
        } else if (PE.isNetObjectPresent("Borland.Delphi.System") &&
            PE.isNetObjectPresent("WinForm")) {
            sVersion = "8 WinForm";
        } else if (PE.isNetObjectPresent("Borland.Delphi.Units")) {
            sVersion = "2005";
        } else if (PE.isNetObjectPresent("Borland.Studio.Delphi")) {
            sName = "Embarcadero Delphi";
            sVersion = "XE*";
        } else {
            return false;
        }

        return true;
    }

    var nSectionOffset = PE.section[0].FileOffset,
        nSectionSize = PE.section[0].FileSize,
        nOffset = nSectionOffset,
        nSize = nSectionSize,
        nOffset2,
        nAddress,
        nLng,
        nLng1,
        bNewVersion = false;

    var nAddressSize = PE.is64() ? 8 : 4;

    while (nSize > 0) {
        nOffset = PE.findSignature(nOffset, nSize, "0708'TControl'");

        if (nOffset == -1) {
            break;
        }

        nAddress = PE.readDword(nOffset + 10);
        nOffset2 = PE.VAToOffset(nAddress);

        if (nOffset2 != -1) {
            if (nOffset2 >= nSectionOffset && (nOffset2 < nSectionOffset + nSectionSize)) {
                nLng = PE.readDword(nOffset2 - 10 * nAddressSize);
                nLng1 = PE.readDword(nOffset2 - 10 * nAddressSize - 11 * nAddressSize);

                switch (nLng) {
                    case 0:
                        if (nLng1 == 0x746E4907) {
                            sVersion = "3";
                        } else if (nLng1 == 0x4F540774) {
                            sVersion = "2";
                        }

                        break;
                    case 0x0B4: sVersion = "C++ Builder"; break;
                    case 0x114: sVersion = "4"; break;
                    case 0x120: sVersion = "5"; break;
                    case 0x128: sVersion = "6 CLX"; break;
                    case 0x12C: sVersion = "7 CLX"; break;
                    case 0x138: sVersion = "Kylix"; break;
                    case 0x15C:
                    case 0x160: sVersion = (nLng1 == 0x40100000 || nLng1 == 0x100000) ? "7" : "6"; break;
                    case 0x164: sVersion = "2005"; break;
                    case 0x190: sVersion = "2006"; break;

                    default:
                        nLng = PE.readDword(nOffset2 - 13 * nAddressSize);

                        if (nLng == 0x1A4) {
                            sName = "Embarcadero Delphi";
                            sVersion = "2009";
                        } else if (nLng == 0x1AC) {
                            var packageInfoResource = PE.resource["PACKAGEINFO"];

                            if (packageInfoResource) {

                                nOffset = packageInfoResource.Offset;
                                nSize = packageInfoResource.Size;

                                if (PE.isSignaturePresent(nOffset, nSize, "'ExcUtils'")) {
                                    sName = "Embarcadero Delphi";
                                    sVersion = "XE";
                                } else if (PE.isSignaturePresent(nOffset, nSize, "'StrUtils'")) {
                                    sName = "Embarcadero Delphi";
                                    sVersion = "2010";
                                } else {
                                    sName = "Embarcadero Delphi";
                                    sVersion = "2010 or XE";
                                }
                            }
                        } else if (nLng == 0x1B4) {
                            // 32
                            sName = "Embarcadero Delphi";
                            sVersion = "XE2-XE4";
                        } else if (nLng == 0x2F0 || nLng == 0x2F8) {
                            // 64
                            sName = "Embarcadero Delphi";
                            sVersion = "XE2";
                        } else if (nLng == 0x1BC) {
                            // 32
                            sName = "Embarcadero Delphi";
                            sVersion = "XE5-XE6";
                            bNewVersion = true;
                        } else {
                            if (nAddressSize == 8) {
                                nLng = PE.readDword(nOffset2 - 16 * nAddressSize);
                                if (nLng == 0x2F8) {
                                    // 64
                                    sName = "Embarcadero Delphi";
                                    sVersion = "XE3-X4";
                                } else if (nLng == 0x308) {
                                    // 64
                                    sName = "Embarcadero Delphi";
                                    sVersion = "XE5-XE6";
                                    bNewVersion = true;
                                }
                            }
                        }
                }

                if (sVersion && !bNewVersion) {
                    return true;
                }
            }
        }

        nOffset++;
        nSize = nSectionSize - (nOffset - nSectionOffset + 1);
    }

    if (PE.resource["PACKAGEINFO"]) {
        nOffset = PE.resource["PACKAGEINFO"].Offset;
        nSize = PE.resource["PACKAGEINFO"].Size;

        if (PE.isSignaturePresent(nOffset, nSize, "'System.SysUtils'")) {
            sName = "Embarcadero Delphi";
            sVersion = "XE2-XE6";
            bNewVersion = true;
        } else if (PE.isSignaturePresent(nOffset, nSize, "'ExcUtils'")) {
            sName = "Embarcadero Delphi";
            sVersion = "XE";
        } else if (PE.isSignaturePresent(nOffset, nSize, "'StrUtils'")) {
            sName = "Embarcadero Delphi";
            sVersion = "2009-2010";
        } else if (PE.isSignaturePresent(nOffset, nSize, "'ImageHlp'")) {
            sVersion = "2006";
        } else if (PE.isSignaturePresent(nOffset, nSize, "'SysInit'")) {
            sVersion = "6-7 or 2005";
        }

        if (sVersion && !bNewVersion) {
            return true;
        }
    } else {
        if (PE.findString(PE.section[0].FileOffset, PE.section[0].FileSize, "Borland\\Delphi") != -1) {
            sVersion = "2-3";
            return true;
        }
    }

    if (bNewVersion) {
        var rdataSection = PE.section[".rdata"];

        if (rdataSection) {
            var nVersionOffset = PE.findString(rdataSection.FileOffset, rdataSection.FileSize, "Embarcadero Delphi for Win");

            if (nVersionOffset != -1) {
                var sCompilerVersion = PE.getString(nVersionOffset + 46, 4);
                if (sCompilerVersion == "28.0") {
                    sVersion = "XE7";
                } else if (sCompilerVersion == "29.0") {
                    sVersion = "XE8";
                } else if (sCompilerVersion == "30.0") {
                    sVersion = "10 Seattle";
                } else if (sCompilerVersion == "31.0") {
                    sVersion = "10.1 Berlin";
                } else if (sCompilerVersion == "32.0") {
                    sVersion = "10.2 Tokyo";
                } else if (sCompilerVersion == "33.0") {
                    sVersion = "10.3 Rio";
                } else if (sCompilerVersion == "34.0") {
                    sVersion = "10.4 Sydney";
                } else if (sCompilerVersion == "35.0") {
                    sVersion = "11.0 Alexandria";
                } else if (sCompilerVersion == "36.0") {
                    sVersion = "12.0 Athens";
                } else {
                    sVersion = "12.X Athens++";
                }
            }
        }

        return true;
    }

    return false;
}

function detect() {
    if (bBorlandC) { // can't be Delphi if it's already C/C++
        return;
    }

    if (PE.resource["PACKAGEINFO"]) {
        bDetected = true;
        getVersion();
    }
    if (PE.resource["DVCLAL"]) {
        bDetected = true;
        getVersion();

        // try to decode DVCLAL (Delphi Visual Component Library Access License) values
        nOffset = PE.getResourceNameOffset("DVCLAL");
        if (PE.compare("A28CDF987B3C3A7926713F090F2A2517", nOffset)) {
            sOptions = "Professional";
        } else if (PE.compare("23785D23B6A5F31943F3400226D111C7", nOffset)) {
            sOptions = "Standard";
        } else if (PE.compare("263D4F38C28237B8F3244203179B3A83", nOffset)) {
            sOptions = "Enterprise";
        }
    } else if (PE.resource["TMAINFORM"]) {
        bDetected = true;
        getVersion();
    } else if (PE.isNet()) {
        if (getVersion()) {
            bDetected = true;
        }
    } else if (PE.compare("0A06'string'", PE.section[0].FileOffset)) {
        sVersion = "2";
        bDetected = true;
    } else if (PE.findSignature(PE.section[0].FileOffset, 100, "07'Boolean'") != -1) {
        bDetected = true;
        getVersion();
    } else if (PE.findSignature(PE.section[0].FileOffset, 100, "06'String'") != -1) {
        bDetected = true;
        getVersion();
    } else if (PE.findSignature(PE.section[0].FileOffset, 0x100, "FF25........8BC0FF25........8BC0")) {
        if (getVersion()) {
            bDetected = true;
        }
    }

    sLang = "Object Pascal (Delphi)";

    return result();
}
