// Detect It Easy: detection rule file
// Author: Levis <levintaeyeon@live.com>, LinXP
// doc-ref: https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html

meta("format", "Java Class File (.CLASS)");

function detect() {
    if (Binary.getSize() >= 8) {
        if (Binary.compare("CAFEBABE")) {
            bDetected = true;

            var nMinor = Binary.read_uint16(0x04, _BE),
                nMajor = Binary.read_uint16(0x06, _BE);

            switch (nMajor) { // https://en.wikipedia.org/wiki/Java_version_history
                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
                case 66: sVersion = "Java SE 22"; break; // March 2024
                case 67: sVersion = "Java SE 23"; break; // September 2024
                case 68: sVersion = "Java SE 24"; break; // March 2025
                case 69: sVersion = "Java SE 25"; break; // September 2025
            }

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

            if (bDetected && Binary.isVerbose()) {
                var nOffset = 0x08;
                var constantPoolCount = Binary.read_uint16(nOffset, _BE);

                nOffset += 2;
                for (pool = 1; pool < constantPoolCount; 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"); }
            }
        }
    }


    sLang = "Java";

    return result();
}