// Detect It Easy: detection rule file format
// Author: Levis <levintaeyeon@live.com>, LinXP
// doc-ref: https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html
init("format", "Java Class File (.CLASS)");

function detect(bShowType, bShowVersion, bShowOptions) {
    if (Binary.getSize() >= 8) {
        if (Binary.compare("CAFEBABE")) {
            var nMinor = Binary.read_uint16(0x04, _BE);
            var nMajor = Binary.read_uint16(0x06, _BE);
            bDetected = true;
            switch (nMajor) {
                case 45:
                    sVersion = "JDK 1.1";
                    break; // February 1997
                case 46:
                    sVersion = "JDK 1.2";
                    break; // December 1998
                case 47:
                    sVersion = "JDK 1.3";
                    break; // May 2000
                case 48:
                    sVersion = "JDK 1.4";
                    break; // February 2002
                case 49:
                    sVersion = "Java SE 5.0";
                    break; // September 2004
                case 50:
                    sVersion = "Java SE 6";
                    break; // December 2006
                case 51:
                    sVersion = "Java SE 7";
                    break; // July 2011
                case 52:
                    sVersion = "Java SE 8";
                    break; // March 2014
                case 53:
                    sVersion = "Java SE 9";
                    break; // September 2017
                case 54:
                    sVersion = "Java SE 10";
                    break; // March 2018
                case 55:
                    sVersion = "Java SE 11";
                    break; // September 2018
                case 56:
                    sVersion = "Java SE 12";
                    break; // March 2019
                case 57:
                    sVersion = "Java SE 13";
                    break; // September 2019
                case 58:
                    sVersion = "Java SE 14";
                    break; // March 2020
                case 59:
                    sVersion = "Java SE 15";
                    break; // September 2020
                case 60:
                    sVersion = "Java SE 16";
                    break; // March 2021
                case 61:
                    sVersion = "Java SE 17";
                    break; // September 2021
                case 62:
                    sVersion = "Java SE 18";
                    break; // March 2022
                case 63:
                    sVersion = "Java SE 19";
                    break; // September 2022
                case 64:
                    sVersion = "Java SE 20";
                    break; // March 2023
                case 65:
                    sVersion = "Java SE 21";
                    break; // September 2023
                default:
                    bDetected = false;
            }

            if ((sVersion != "") && (nMinor)) {
                sVersion += "." + nMinor;
            }

            if (bDetected && Binary.isVerbose()) {
                var nOffset = 0x08;
                var constant_pool_count = Binary.read_uint16(nOffset, _BE);
                nOffset += 2;
                for (pool = 1; pool < constant_pool_count; pool++) {
                    switch (Binary.read_uint8(nOffset)) // Tag
                    {
                        case 1: // CONSTANT_Utf8
                            nOffset += 1 + 2 + Binary.read_uint16(nOffset + 1, _BE);
                            break;
                        case 3: // CONSTANT_Integer
                        case 4: // CONSTANT_Float
                            nOffset += 1 + 4;
                            break;
                        case 5: // CONSTANT_Long
                        case 6: // CONSTANT_Double
                            nOffset += 1 + 4 + 4;
                            break;
                        case 7: // CONSTANT_Class
                        case 8: // CONSTANT_String
                        case 16: // CONSTANT_MethodType
                            nOffset += 1 + 2;
                            break;
                        case 9: // CONSTANT_Fieldref
                        case 10: // CONSTANT_Methodref
                        case 11: // CONSTANT_InterfaceMethodref
                        case 12: // CONSTANT_NameAndType
                        case 18: // CONSTANT_InvokeDynamic
                            nOffset += 1 + 2 + 2;
                            break;
                        case 15: // CONSTANT_MethodHandle_info
                            nOffset += 1 + 1 + 2;
                            break;
                        default:
                            sOption("err unk tag:" + nOffset);
                    }
                }
                var nAccessFlagss = Binary.read_uint16(nOffset, _BE);
                if (nAccessFlagss & 0x0001) { sOption("ACC_PUBLIC"); }
                if (nAccessFlagss & 0x0002) { sOption("ACC_PRIVATE"); }
                if (nAccessFlagss & 0x0004) { sOption("ACC_PROTECTED"); }
                if (nAccessFlagss & 0x0008) { sOption("ACC_STATIC"); }
                if (nAccessFlagss & 0x0010) { sOption("ACC_FINAL"); }
                if (nAccessFlagss & 0x0020) { sOption("ACC_SUPER"); }
                if (nAccessFlagss & 0x0040) { sOption("ACC_VOLATILE"); }
                if (nAccessFlagss & 0x0080) { sOption("ACC_TRANSIENT"); }
                if (nAccessFlagss & 0x0100) { sOption("ACC_NATIVE"); }
                if (nAccessFlagss & 0x0200) { sOption("ACC_INTERFACE"); }
                if (nAccessFlagss & 0x0400) { sOption("ACC_ABSTRACT"); }
                if (nAccessFlagss & 0x0800) { sOption("ACC_STRICT"); }
            }
        }
    }


    _setLang("Java", bDetected);
    return result(bShowType, bShowVersion, bShowOptions);
}