// Simple recursive parser for ASN1 BER. private MHParseNode DoParse() { byte ch; // Tag class int tagClass = Universal; // Byte count of end of this item. Set to INDEFINITE_LENGTH if the length is Indefinite. int endOfItem; int tagNumber = 0; // Read the first character. ch = GetNextChar(); // ASN1 Coding rules: Top two bits (0 and 1) indicate the tag class. // 0x00 - Universal, 0x40 - Application, 0x80 - Context-specific, 0xC0 - Private // We only use Universal and Context. switch (ch & 0xC0) { case 0x00: // Universal tagClass = Universal; break; case 0x80: tagClass = Context; break; default: throw new MHEGException("Invalid tag class = " + ch); } // Bit 2 indicates whether it is a simple or compound type. Not used. // Lower bits are the tag number. tagNumber = ch & 0x1f; if (tagNumber == 0x1f) // Except that if it is 0x1F then the tag is encoded in the following bytes. { tagNumber = 0; do { ch = GetNextChar(); tagNumber = (tagNumber << 7) | (ch & 0x7f); } while ((ch & 0x80) != 0); // Top bit set means there's more to come. } // Next byte is the length. If it is less than 128 it is the actual length, otherwise it // gives the number of bytes containing the length, except that if this is zero the item // has an "indefinite" length and is terminated by two zero bytes. ch = GetNextChar(); if ((ch & 0x80) != 0) { int lengthOfLength = ch & 0x7f; if (lengthOfLength == 0) { endOfItem = INDEFINITE_LENGTH; } else { endOfItem = 0; while ((lengthOfLength--) != 0) { ch = GetNextChar(); endOfItem = (endOfItem << 8) | ch; } endOfItem += m_p; } } else { endOfItem = ch + m_p; } if (tagClass == Context) { MHPTagged pNode = new MHPTagged(tagNumber); // The argument here depends on the particular tag we're processing. switch (tagNumber) { case ASN1Codes.C_MULTIPLE_SELECTION: case ASN1Codes.C_OBSCURED_INPUT: case ASN1Codes.C_INITIALLY_AVAILABLE: case ASN1Codes.C_WRAP_AROUND: case ASN1Codes.C_TEXT_WRAPPING: case ASN1Codes.C_INITIALLY_ACTIVE: case ASN1Codes.C_MOVING_CURSOR: case ASN1Codes.C_SHARED: case ASN1Codes.C_ENGINE_RESP: case ASN1Codes.C_TILING: case ASN1Codes.C_BORDERED_BOUNDING_BOX: { // BOOL // If there is no argument we need to indicate that so that it gets // the correct default value. if (m_p != endOfItem) { int intVal = ParseInt(endOfItem); // May raise an exception pNode.AddArg(new MHPBool(intVal != 0)); } break; } case ASN1Codes.C_INPUT_TYPE: case ASN1Codes.C_SLIDER_STYLE: case ASN1Codes.C_TERMINATION: case ASN1Codes.C_ORIENTATION: case ASN1Codes.C_HORIZONTAL_JUSTIFICATION: case ASN1Codes.C_BUTTON_STYLE: case ASN1Codes.C_START_CORNER: case ASN1Codes.C_LINE_ORIENTATION: case ASN1Codes.C_VERTICAL_JUSTIFICATION: case ASN1Codes.C_STORAGE: { // ENUM if (m_p != endOfItem) { int intVal = ParseInt(endOfItem); // May raise an exception pNode.AddArg(new MHPEnum(intVal)); } break; } case ASN1Codes.C_INITIAL_PORTION: case ASN1Codes.C_STEP_SIZE: case ASN1Codes.C_INPUT_EVENT_REGISTER: case ASN1Codes.C_INITIAL_VALUE: case ASN1Codes.C_IP_CONTENT_HOOK: case ASN1Codes.C_MAX_VALUE: case ASN1Codes.C_MIN_VALUE: case ASN1Codes.C_LINE_ART_CONTENT_HOOK: case ASN1Codes.C_BITMAP_CONTENT_HOOK: case ASN1Codes.C_TEXT_CONTENT_HOOK: case ASN1Codes.C_STREAM_CONTENT_HOOK: case ASN1Codes.C_MAX_LENGTH: case ASN1Codes.C_CHARACTER_SET: case ASN1Codes.C_ORIGINAL_TRANSPARENCY: case ASN1Codes.C_ORIGINAL_GC_PRIORITY: case ASN1Codes.C_LOOPING: case ASN1Codes.C_ORIGINAL_LINE_STYLE: case ASN1Codes.C_STANDARD_VERSION: case ASN1Codes.C_ORIGINAL_LINE_WIDTH: case ASN1Codes.C_CONTENT_HOOK: case ASN1Codes.C_CONTENT_CACHE_PRIORITY: case ASN1Codes.C_COMPONENT_TAG: case ASN1Codes.C_ORIGINAL_VOLUME: case ASN1Codes.C_PROGRAM_CONNECTION_TAG: case ASN1Codes.C_CONTENT_SIZE: { // INT if (m_p != endOfItem) { int intVal = ParseInt(endOfItem); // May raise an exception pNode.AddArg(new MHPInt(intVal)); } break; } case ASN1Codes.C_OBJECT_INFORMATION: case ASN1Codes.C_CONTENT_REFERENCE: case ASN1Codes.C_FONT_ATTRIBUTES: case ASN1Codes.C_CHAR_LIST: case ASN1Codes.C_NAME: case ASN1Codes.C_ORIGINAL_LABEL: { // STRING // Unlike INT, BOOL and ENUM we can't distinguish an empty string // from a missing string. MHOctetString str = new MHOctetString(); ParseString(endOfItem, str); pNode.AddArg(new MHPString(str)); break; } default: { // Everything else has either no argument or is self-describing // TODO: Handle indefinite length. Logging.Assert(endOfItem != INDEFINITE_LENGTH); // For the moment. while (m_p < endOfItem) { pNode.AddArg(DoParse()); } break; } } return(pNode); } else // Universal - i.e. a primitive type. // Tag values { switch (tagNumber) { case ASN1Codes.U_BOOL: // Boolean { int intVal = ParseInt(endOfItem); return(new MHPBool(intVal != 0)); } case ASN1Codes.U_INT: // Integer { int intVal = ParseInt(endOfItem); return(new MHPInt(intVal)); } case ASN1Codes.U_ENUM: // ENUM { int intVal = ParseInt(endOfItem); return(new MHPEnum(intVal)); } case ASN1Codes.U_STRING: // String { MHOctetString str = new MHOctetString(); ParseString(endOfItem, str); return(new MHPString(str)); } case ASN1Codes.U_NULL: // ASN1 NULL { return(new MHPNull()); } case ASN1Codes.U_SEQUENCE: // Sequence { MHParseSequence pNode = new MHParseSequence(); Logging.Assert(endOfItem != INDEFINITE_LENGTH); // TODO: Implement this. while (m_p < endOfItem) { pNode.Append(DoParse()); } Logging.Assert(m_p == endOfItem); return(pNode); } default: Logging.Assert(false); throw new MHEGException("Unknown universal"); } } }
private MHParseNode DoParse() { MHParseNode pRes = null; switch (m_nType) { case PTStartSection: // Open curly bracket { NextSym(); // Should be followed by a tag. if (m_nType != PTTag) { Error("Expected ':' after '{'"); } MHPTagged pTag = new MHPTagged(m_nTag); pRes = pTag; NextSym(); while (m_nType != PTEndSection) { pTag.AddArg(DoParse()); } NextSym(); // Remove the close curly bracket. break; } case PTTag: // Tag on its own. { int nTag = m_nTag; MHPTagged pTag = new MHPTagged(nTag); pRes = pTag; NextSym(); switch (nTag) { case ASN1Codes.C_ITEMS: case ASN1Codes.C_LINK_EFFECT: case ASN1Codes.C_ACTIVATE: case ASN1Codes.C_ADD: case ASN1Codes.C_ADD_ITEM: case ASN1Codes.C_APPEND: case ASN1Codes.C_BRING_TO_FRONT: case ASN1Codes.C_CALL: case ASN1Codes.C_CALL_ACTION_SLOT: case ASN1Codes.C_CLEAR: case ASN1Codes.C_CLONE: case ASN1Codes.C_CLOSE_CONNECTION: case ASN1Codes.C_DEACTIVATE: case ASN1Codes.C_DEL_ITEM: case ASN1Codes.C_DESELECT: case ASN1Codes.C_DESELECT_ITEM: case ASN1Codes.C_DIVIDE: case ASN1Codes.C_DRAW_ARC: case ASN1Codes.C_DRAW_LINE: case ASN1Codes.C_DRAW_OVAL: case ASN1Codes.C_DRAW_POLYGON: case ASN1Codes.C_DRAW_POLYLINE: case ASN1Codes.C_DRAW_RECTANGLE: case ASN1Codes.C_DRAW_SECTOR: case ASN1Codes.C_FORK: case ASN1Codes.C_GET_AVAILABILITY_STATUS: case ASN1Codes.C_GET_BOX_SIZE: case ASN1Codes.C_GET_CELL_ITEM: case ASN1Codes.C_GET_CURSOR_POSITION: case ASN1Codes.C_GET_ENGINE_SUPPORT: case ASN1Codes.C_GET_ENTRY_POINT: case ASN1Codes.C_GET_FILL_COLOUR: case ASN1Codes.C_GET_FIRST_ITEM: case ASN1Codes.C_GET_HIGHLIGHT_STATUS: case ASN1Codes.C_GET_INTERACTION_STATUS: case ASN1Codes.C_GET_ITEM_STATUS: case ASN1Codes.C_GET_LABEL: case ASN1Codes.C_GET_LAST_ANCHOR_FIRED: case ASN1Codes.C_GET_LINE_COLOUR: case ASN1Codes.C_GET_LINE_STYLE: case ASN1Codes.C_GET_LINE_WIDTH: case ASN1Codes.C_GET_LIST_ITEM: case ASN1Codes.C_GET_LIST_SIZE: case ASN1Codes.C_GET_OVERWRITE_MODE: case ASN1Codes.C_GET_PORTION: case ASN1Codes.C_GET_POSITION: case ASN1Codes.C_GET_RUNNING_STATUS: case ASN1Codes.C_GET_SELECTION_STATUS: case ASN1Codes.C_GET_SLIDER_VALUE: case ASN1Codes.C_GET_TEXT_CONTENT: case ASN1Codes.C_GET_TEXT_DATA: case ASN1Codes.C_GET_TOKEN_POSITION: case ASN1Codes.C_GET_VOLUME: case ASN1Codes.C_LAUNCH: case ASN1Codes.C_LOCK_SCREEN: case ASN1Codes.C_MODULO: case ASN1Codes.C_MOVE: case ASN1Codes.C_MOVE_TO: case ASN1Codes.C_MULTIPLY: case ASN1Codes.C_OPEN_CONNECTION: case ASN1Codes.C_PRELOAD: case ASN1Codes.C_PUT_BEFORE: case ASN1Codes.C_PUT_BEHIND: case ASN1Codes.C_QUIT: case ASN1Codes.C_READ_PERSISTENT: case ASN1Codes.C_RUN: case ASN1Codes.C_SCALE_BITMAP: case ASN1Codes.C_SCALE_VIDEO: case ASN1Codes.C_SCROLL_ITEMS: case ASN1Codes.C_SELECT: case ASN1Codes.C_SELECT_ITEM: case ASN1Codes.C_SEND_EVENT: case ASN1Codes.C_SEND_TO_BACK: case ASN1Codes.C_SET_BOX_SIZE: case ASN1Codes.C_SET_CACHE_PRIORITY: case ASN1Codes.C_SET_COUNTER_END_POSITION: case ASN1Codes.C_SET_COUNTER_POSITION: case ASN1Codes.C_SET_COUNTER_TRIGGER: case ASN1Codes.C_SET_CURSOR_POSITION: case ASN1Codes.C_SET_CURSOR_SHAPE: case ASN1Codes.C_SET_DATA: case ASN1Codes.C_SET_ENTRY_POINT: case ASN1Codes.C_SET_FILL_COLOUR: case ASN1Codes.C_SET_FIRST_ITEM: case ASN1Codes.C_SET_FONT_REF: case ASN1Codes.C_SET_HIGHLIGHT_STATUS: case ASN1Codes.C_SET_INTERACTION_STATUS: case ASN1Codes.C_SET_LABEL: case ASN1Codes.C_SET_LINE_COLOUR: case ASN1Codes.C_SET_LINE_STYLE: case ASN1Codes.C_SET_LINE_WIDTH: case ASN1Codes.C_SET_OVERWRITE_MODE: case ASN1Codes.C_SET_PALETTE_REF: case ASN1Codes.C_SET_PORTION: case ASN1Codes.C_SET_POSITION: case ASN1Codes.C_SET_SLIDER_VALUE: case ASN1Codes.C_SET_SPEED: case ASN1Codes.C_SET_TIMER: case ASN1Codes.C_SET_TRANSPARENCY: case ASN1Codes.C_SET_VARIABLE: case ASN1Codes.C_SET_VOLUME: case ASN1Codes.C_SPAWN: case ASN1Codes.C_STEP: case ASN1Codes.C_STOP: case ASN1Codes.C_STORE_PERSISTENT: case ASN1Codes.C_SUBTRACT: case ASN1Codes.C_TEST_VARIABLE: case ASN1Codes.C_TOGGLE: case ASN1Codes.C_TOGGLE_ITEM: case ASN1Codes.C_TRANSITION_TO: case ASN1Codes.C_UNLOAD: case ASN1Codes.C_UNLOCK_SCREEN: case ASN1Codes.C_CONTENT_REFERENCE: case ASN1Codes.C_TOKEN_GROUP_ITEMS: case ASN1Codes.C_POSITIONS: case ASN1Codes.C_MULTIPLEX: { // These are parenthesised in the text form. We have to remove the // parentheses otherwise we will return a sequence which will not be // be compatible with the binary form. if (m_nType != PTStartSeq) { Error("Expected '('"); } NextSym(); while (m_nType != PTEndSeq) { pTag.AddArg(DoParse()); } NextSym(); // Remove the close parenthesis. break; } case ASN1Codes.C_ORIGINAL_CONTENT: case ASN1Codes.C_NEW_GENERIC_BOOLEAN: case ASN1Codes.C_NEW_GENERIC_INTEGER: case ASN1Codes.C_NEW_GENERIC_OCTETSTRING: case ASN1Codes.C_NEW_GENERIC_OBJECT_REF: case ASN1Codes.C_NEW_GENERIC_CONTENT_REF: case ASN1Codes.C_ORIGINAL_VALUE: case ASN1Codes.C_INDIRECTREFERENCE: // These always have an argument which may be a tagged item. { // Is it always the case that there is at least one argument so if we haven't // had any arguments yet we should always process a tag as an argument? pTag.AddArg(DoParse()); break; } default: // This can be followed by an int, etc but a new tag is dealt with by the caller. while (m_nType == PTBool || m_nType == PTInt || m_nType == PTString || m_nType == PTEnum || m_nType == PTStartSeq) { pTag.AddArg(DoParse()); } break; } break; } case PTInt: { pRes = new MHPInt(m_nInt); NextSym(); break; } case PTBool: { pRes = new MHPBool(m_fBool); NextSym(); break; } case PTString: { MHOctetString str = new MHOctetString(); // str.Copy(MHOctetString((const char *)m_String, m_nStringLength)); str.Copy(m_String); pRes = new MHPString(str); NextSym(); break; } case PTEnum: { pRes = new MHPEnum(m_nInt); // ASSERT(m_nInt > 0); NextSym(); break; } case PTNull: { pRes = new MHPNull(); NextSym(); break; } case PTStartSeq: // Open parenthesis. { MHParseSequence pSeq = new MHParseSequence(); pRes = pSeq; NextSym(); while (m_nType != PTEndSeq) { pSeq.Append(DoParse()); } NextSym(); // Remove the close parenthesis. break; } default: Error("Unexpected symbol"); break; } return(pRes); }