// Return the value, looking up any indirect ref. void MHGenericOctetString::GetValue(MHOctetString &str, MHEngine *engine) const { if (m_fIsDirect) str.Copy(m_Direct); else { MHUnion result; MHRoot *pBase = engine->FindObject(m_Indirect); pBase->GetVariableValue(result, engine); // From my reading of the MHEG documents implicit conversion is only // performed when assigning variables. Nevertheless the Channel 4 // Teletext assumes that implicit conversion takes place here as well. if (result.m_Type == MHUnion::U_Int) { // Implicit conversion of int to string. char buff[30]; // 30 chars is more than enough. #ifdef WIN32 _snprintf(buff, sizeof(buff), "%0d", result.m_nIntVal); #else snprintf(buff, sizeof(buff), "%0d", result.m_nIntVal); #endif str.Copy(buff); } else { result.CheckType(MHUnion::U_String); str.Copy(result.m_StrVal); } } }
// Construct a substring. MHOctetString::MHOctetString(const MHOctetString &str, int nOffset, int nLen) { if (nLen < 0) { nLen = str.Size() - nOffset; // The rest of the string. } if (nLen < 0) { nLen = 0; } if (nLen > str.Size()) { nLen = str.Size(); } m_nLength = nLen; if (nLen == 0) { m_pChars = 0; } else { m_pChars = (unsigned char *)malloc(nLen + 1); if (! m_pChars) { throw "Out of memory"; } memcpy(m_pChars, str.m_pChars + nOffset, nLen); } }
void MHEngine::GetDefaultFontAttrs(MHOctetString &str) { MHApplication *pApp = CurrentApp(); if (pApp && pApp->m_FontAttrs.Size() > 0) { str.Copy(pApp->m_FontAttrs); } else { str.Copy("plain.24.24.0"); // TODO: Check this. } }
// Create a canonical path name. The rules are given in the UK MHEG document. QString MHEngine::GetPathName(const MHOctetString &str) { if (str.Size() == 0) return QString(); QString csPath = QString::fromUtf8((const char *)str.Bytes(), str.Size()); switch (PathProtocol(csPath)) { default: case kProtoUnknown: case kProtoHybrid: case kProtoHTTP: case kProtoCI: return csPath; case kProtoDSM: break; } if (csPath.startsWith("DSM:")) csPath = csPath.mid(4); // Remove DSM: else if (csPath.startsWith("~")) csPath = csPath.mid(1); // Remove ~ if (!csPath.startsWith("//")) { // Add the current application's path name if (CurrentApp()) { csPath = CurrentApp()->m_Path + csPath; } } // Remove any occurrences of x/../ int nPos; while ((nPos = csPath.indexOf("/../")) >= 0) { int nEnd = nPos + 4; while (nPos >= 1 && csPath[nPos-1] != '/') { nPos--; } csPath = csPath.left(nPos) + csPath.mid(nEnd); } return csPath; }
// Extract a string value. static void GetString(MHParameter *parm, MHOctetString &str, MHEngine *engine) { MHUnion un; un.GetValueFrom(*parm, engine); un.CheckType(MHUnion::U_String); str.Copy(un.m_StrVal); }
// Parse a string argument. ASN1 strings can include nulls as valid characters. void MHParseBinary::ParseString(int endStr, MHOctetString &str) { // TODO: Don't deal with indefinite length at the moment. if (endStr == INDEFINITE_LENGTH) { MHERROR("Indefinite length strings are not implemented"); } int nLength = endStr - m_p; unsigned char *stringValue = (unsigned char *)malloc(nLength + 1); if (stringValue == NULL) { MHERROR("Out of memory"); } unsigned char *p = stringValue; while (m_p < endStr) { *p++ = GetNextChar(); } str.Copy(MHOctetString((const char *)stringValue, nLength)); free(stringValue); }
// String void MHParseNode::GetStringValue(MHOctetString &str) { if (m_nNodeType != PNString) { Failure("Expected string"); } str.Copy(((MHPString *)this)->m_Value); }
// Find out what we support. bool MHEngine::GetEngineSupport(const MHOctetString &feature) { QString csFeat = QString::fromUtf8((const char *)feature.Bytes(), feature.Size()); QStringList strings = csFeat.split(QRegExp("[\\(\\,\\)]")); MHLOG(MHLogNotifications, "NOTE GetEngineSupport " + csFeat); if (strings[0] == "ApplicationStacking" || strings[0] == "ASt") { return true; } // We're required to support cloning for Text, Bitmap and Rectangle. if (strings[0] == "Cloning" || strings[0] == "Clo") { return true; } if (strings[0] == "SceneCoordinateSystem" || strings[0] == "SCS") { if (strings.count() >= 3 && strings[1] == "720" && strings[2] == "576") { return true; } else { return false; } // I've also seen SceneCoordinateSystem(1,1) } if (strings[0] == "MultipleAudioStreams" || strings[0] == "MAS") { if (strings.count() >= 2 && (strings[1] == "0" || strings[1] == "1")) { return true; } else { return false; } } if (strings[0] == "MultipleVideoStreams" || strings[0] == "MVS") { if (strings.count() >= 2 && (strings[1] == "0" || strings[1] == "1")) { return true; } else { return false; } } // We're supposed to return true for all values of N if (strings[0] == "OverlappingVisibles" || strings[0] == "OvV") { return true; } if (strings[0] == "SceneAspectRatio" || strings[0] == "SAR") { if (strings.count() < 3) { return false; } else if ((strings[1] == "4" && strings[2] == "3") || (strings[1] == "16" && strings[2] == "9")) { return true; } else { return false; } } // We're supposed to support these at least. May also support(10,1440,1152) if (strings[0] == "VideoScaling" || strings[0] == "VSc") { if (strings.count() < 4 || strings[1] != "10") { return false; } else if ((strings[2] == "720" && strings[3] == "576") || (strings[2] == "360" && strings[3] == "288")) { return true; } else { return false; } } if (strings[0] == "BitmapScaling" || strings[0] == "BSc") { if (strings.count() < 4 || strings[1] != "2") { return false; } else if ((strings[2] == "720" && strings[3] == "576") || (strings[2] == "360" && strings[3] == "288")) { return true; } else { return false; } } // I think we only support the video fully on screen if (strings[0] == "VideoDecodeOffset" || strings[0] == "VDO") { if (strings.count() >= 3 && strings[1] == "10" && strings[1] == "0") { return true; } else { return false; } } // We support bitmaps that are partially off screen (don't we?) if (strings[0] == "BitmapDecodeOffset" || strings[0] == "BDO") { if (strings.count() >= 3 && strings[1] == "2" && (strings[2] == "0" || strings[2] == "1")) { return true; } else if (strings.count() >= 2 && (strings[1] == "4" || strings[1] == "6")) { return true; } else { return false; } } if (strings[0] == "UKEngineProfile" || strings[0] == "UniversalEngineProfile" || strings[0] == "UEP") { if (strings.count() < 2) { return false; } if (strings[1] == MHEGEngineProviderIdString) { return true; } if (strings[1] == m_Context->GetReceiverId()) { return true; } if (strings[1] == m_Context->GetDSMCCId()) { return true; } // The UK profile 1.06 seems a bit confused on this point. It is not clear whether // we are supposed to return true for UKEngineProfile(2) or not. if (strings[1] == "2") { return true; } else { return false; } } // InteractionChannelExtension. if (strings[0] == "ICProfile" || strings[0] == "ICP") { if (strings.count() != 2) return false; if (strings[1] == "0") return true; // // InteractionChannelExtension. if (strings[1] == "1") return false; // ICStreamingExtension. return false; } // Otherwise return false. return false; }
// UK MHEG. Interpret the font attributes. static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace) { // Set the defaults. style = 0; size = 0x18; lineSpace = 0x18; letterSpace = 0; if (attrs.Size() == 5) // Short form. { style = attrs.GetAt(0); // Only the bottom nibble is significant. size = attrs.GetAt(1); lineSpace = attrs.GetAt(2); // Is this big-endian or little-endian? Assume big. letterSpace = attrs.GetAt(3) * 256 + attrs.GetAt(4); if (letterSpace > 32767) { letterSpace -= 65536; // Signed. } } else // Textual form. { const unsigned char *str = attrs.Bytes(); char *p = (char *)str; char *q = strchr(p, '.'); // Find the terminating dot if (q != NULL) // plain, italic etc. { if (q - p == 6 && strncmp(p, "italic", 6) == 0) { style = 1; } else if (q - p == 4 && strncmp(p, "bold", 4) == 0) { style = 2; } else if (q - p == 11 && strncmp(p, "bold-italic", 11) == 0) { style = 3; } // else it's plain. p = q + 1; q = strchr(p, '.'); // Find the next dot. } if (q != NULL) // Size { size = atoi(p); if (size == 0) { size = 0x18; } p = q + 1; q = strchr(p, '.'); // Find the next dot. } if (q != NULL) // lineSpacing { lineSpace = atoi(p); if (lineSpace == 0) { lineSpace = 0x18; } p = q + 1; q = strchr(p, '.'); // Find the next dot. } if (q != NULL) // letter spacing. May be zero or negative { letterSpace = atoi(p); } } }
void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence<MHParameter *> &args, MHEngine *engine) { if (! m_fAvailable) { Preparation(engine); } // if (m_fRunning) return; // Strictly speaking there should be only one instance of a program running at a time. Activation(engine); MHLOG(MHLogDetail, QString("Calling program %1").arg(m_Name.Printable())); try { // Run the code. if (m_Name.Equal("GCD")) // GetCurrentDate - returns local time. { if (args.Size() == 2) { time_t epochSeconds = 0; short int timeZone = 0; #if HAVE_GETTIMEOFDAY struct timeval time; struct timezone zone; if (gettimeofday(&time, &zone) == -1) { MHLOG(MHLogDetail, QString("gettimeofday() failed")); } epochSeconds = time.tv_sec; timeZone = zone.tz_minuteswest; #elif HAVE_FTIME struct timeb timebuffer; if (ftime(&timebuffer) == -1) { MHLOG(MHLogDetail, QString("ftime() failed")); } epochSeconds = timebuffer.time; timeZone = timebuffer.timezone; #else #error Configuration error? No ftime() or gettimeofday()? #endif // Adjust the time to local. TODO: Check this. epochSeconds -= timeZone * 60; // Time as seconds since midnight. int nTimeAsSecs = epochSeconds % (24 * 60 * 60); // Modified Julian date as number of days since 17th November 1858. // 1st Jan 1970 was date 40587. int nModJulianDate = 40587 + epochSeconds / (24 * 60 * 60); engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(nModJulianDate); engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(nTimeAsSecs); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("FDa")) // FormatDate { if (args.Size() == 4) { // This is a bit like strftime but not quite. MHOctetString format; GetString(args.GetAt(0), format, engine); int date = GetInt(args.GetAt(1), engine); // As produced in GCD int time = GetInt(args.GetAt(2), engine); // Convert to a Unix date (secs since 1st Jan 1970) but adjusted for time zone. time_t timet = (date - 40587) * (24 * 60 * 60) + time; struct tm *timeStr = gmtime(&timet); MHOctetString result; for (int i = 0; i < format.Size(); i++) { unsigned char ch = format.GetAt(i); char buffer[5]; // Largest text is 4 chars for a year + null terminator if (ch == '%') { i++; if (i == format.Size()) { break; } ch = format.GetAt(i); buffer[0] = 0; switch (ch) { case 'Y': sprintf(buffer, "%04d", timeStr->tm_year + 1900); break; case 'y': sprintf(buffer, "%02d", timeStr->tm_year % 100); break; case 'X': sprintf(buffer, "%02d", timeStr->tm_mon + 1); break; case 'x': sprintf(buffer, "%1d", timeStr->tm_mon + 1); break; case 'D': sprintf(buffer, "%02d", timeStr->tm_mday); break; case 'd': sprintf(buffer, "%1d", timeStr->tm_mday); break; case 'H': sprintf(buffer, "%02d", timeStr->tm_hour); break; case 'h': sprintf(buffer, "%1d", timeStr->tm_hour); break; case 'I': if (timeStr->tm_hour == 12 || timeStr->tm_hour == 0) { strcpy(buffer, "12"); } else { sprintf(buffer, "%02d", timeStr->tm_hour % 12); } break; case 'i': if (timeStr->tm_hour == 12 || timeStr->tm_hour == 0) { strcpy(buffer, "12"); } else { sprintf(buffer, "%1d", timeStr->tm_hour % 12); } break; case 'M': sprintf(buffer, "%02d", timeStr->tm_min); break; case 'm': sprintf(buffer, "%1d", timeStr->tm_min); break; case 'S': sprintf(buffer, "%02d", timeStr->tm_sec); break; case 's': sprintf(buffer, "%1d", timeStr->tm_sec); break; // TODO: These really should be localised. case 'A': if (timeStr->tm_hour < 12) { strcpy(buffer, "AM"); } else { strcpy(buffer, "PM"); } break; case 'a': if (timeStr->tm_hour < 12) { strcpy(buffer, "am"); } else { strcpy(buffer, "pm"); } break; default: buffer[0] = ch; buffer[1] = 0; } result.Append(buffer); } else { result.Append(MHOctetString(&ch, 1)); } } MHParameter *pResString = args.GetAt(3); engine->FindObject(*(pResString->GetReference()))->SetVariableValue(result); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("GDW")) // GetDayOfWeek - returns the day of week that the date occurred on. { if (args.Size() == 2) { int date = GetInt(args.GetAt(0), engine); // Date as produced in GCD // Convert to a Unix date (secs since 1st Jan 1970) but adjusted for time zone. time_t timet = (date - 40587) * (24 * 60 * 60); struct tm *timeStr = gmtime(&timet); // 0 => Sunday, 1 => Monday etc. engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(timeStr->tm_wday); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("Rnd")) // Random { if (args.Size() == 2) { int nLimit = GetInt(args.GetAt(0), engine); MHParameter *pResInt = args.GetAt(1); int r = random() % (nLimit + 1); engine->FindObject( *(pResInt->GetReference()))->SetVariableValue(r); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("CTC")) // CastToContentRef { // Converts a string to a ContentRef. if (args.Size() == 2) { MHOctetString string; GetString(args.GetAt(0), string, engine); MHContentRef result; result.m_ContentRef.Copy(string); engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(result); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("CTO")) // CastToObjectRef { // Converts a string and an integer to an ObjectRef. if (args.Size() == 3) { MHObjectRef result; GetString(args.GetAt(0), result.m_GroupId, engine); result.m_nObjectNo = GetInt(args.GetAt(1), engine); engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(result); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("GSL")) // GetStringLength { if (args.Size() == 2) { // Find a substring within a string and return an index to the position. MHOctetString string; GetString(args.GetAt(0), string, engine); MHParameter *pResInt = args.GetAt(1); SetSuccessFlag(success, true, engine); engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(string.Size()); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("GSS")) // GetSubString { if (args.Size() == 4) // Extract a sub-string from a string. { MHOctetString string; GetString(args.GetAt(0), string, engine); int nBeginExtract = GetInt(args.GetAt(1), engine); int nEndExtract = GetInt(args.GetAt(2), engine); if (nBeginExtract < 1) { nBeginExtract = 1; } if (nBeginExtract > string.Size()) { nBeginExtract = string.Size(); } if (nEndExtract < 1) { nEndExtract = 1; } if (nEndExtract > string.Size()) { nEndExtract = string.Size(); } MHParameter *pResString = args.GetAt(3); // Returns beginExtract to endExtract inclusive. engine->FindObject(*(pResString->GetReference()))->SetVariableValue( MHOctetString(string, nBeginExtract - 1, nEndExtract - nBeginExtract + 1)); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("SSS")) // SearchSubString { if (args.Size() == 4) { // Find a substring within a string and return an index to the position. MHOctetString string, searchString; GetString(args.GetAt(0), string, engine); int nStart = GetInt(args.GetAt(1), engine); if (nStart < 1) { nStart = 1; } GetString(args.GetAt(2), searchString, engine); // Strings are indexed from one. int nPos; for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++) { int i; for (i = 0; i < searchString.Size(); i++) { if (searchString.GetAt(i) != string.GetAt(i + nPos)) { break; } } if (i == searchString.Size()) { break; // Found a match. } } // Set the result. MHParameter *pResInt = args.GetAt(3); SetSuccessFlag(success, true, engine); // Set this first. if (nPos <= string.Size() - searchString.Size()) // Found { // Set the index to the position of the string, counting from 1. engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1); } else // Not found. Set the result index to -1 { engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1); } } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("SES")) // SearchAndExtractSubString { if (args.Size() == 5) { // Find a substring within a string and return an index to the position // and the prefix to the substring. MHOctetString string, searchString; GetString(args.GetAt(0), string, engine); int nStart = GetInt(args.GetAt(1), engine); if (nStart < 1) { nStart = 1; } GetString(args.GetAt(2), searchString, engine); // Strings are indexed from one. int nPos; for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++) { int i; for (i = 0; i < searchString.Size(); i++) { if (searchString.GetAt(i) != string.GetAt(i + nPos)) { break; // Doesn't match } } if (i == searchString.Size()) { break; // Found a match. } } // Set the results. MHParameter *pResString = args.GetAt(3); MHParameter *pResInt = args.GetAt(4); SetSuccessFlag(success, true, engine); // Set this first. if (nPos <= string.Size() - searchString.Size()) // Found { // Set the index to the position AFTER the string, counting from 1. engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1 + searchString.Size()); // Return the sequence from nStart-1 of length nPos-nStart+1 MHOctetString resultString(string, nStart - 1, nPos - nStart + 1); engine->FindObject(*(pResString->GetReference()))->SetVariableValue(resultString); } else // Not found. Set the result string to empty and the result index to -1 { engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1); engine->FindObject(*(pResString->GetReference()))->SetVariableValue(MHOctetString("")); } } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("GSI")) // SI_GetServiceIndex { // Returns an index indicating the service if (args.Size() == 2) { MHOctetString string; GetString(args.GetAt(0), string, engine); MHParameter *pResInt = args.GetAt(1); // The format of the service is dvb://netID.[transPortID].serviceID // where the IDs are in hex. // or rec://svc/lcn/N where N is the "logical channel number" i.e. the Freeview channel. QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size()); int nResult = engine->GetContext()->GetChannelIndex(str); engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nResult); SetSuccessFlag(success, nResult >= 0, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("TIn")) // SI_TuneIndex - Fork not allowed { // Tunes to an index returned by GSI if (args.Size() == 1) { int nChannel = GetInt(args.GetAt(0), engine); bool res = nChannel >= 0 ? engine->GetContext()->TuneTo( nChannel, engine->GetTuneInfo()) : false; SetSuccessFlag(success, res, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("TII")) // SI_TuneIndexInfo { // Indicates whether to perform a subsequent TIn quietly or normally. if (args.Size() == 1) { int tuneinfo = GetInt(args.GetAt(0), engine); engine->SetTuneInfo(tuneinfo); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("BSI")) // SI_GetBasicSI { // Returns basic SI information about the service indicated by an index // returned by GSI. // Returns networkID, origNetworkID, transportStreamID, serviceID if (args.Size() == 5) { int channelId = GetInt(args.GetAt(0), engine); int netId, origNetId, transportId, serviceId; // Look the information up in the database. bool res = engine->GetContext()->GetServiceInfo(channelId, netId, origNetId, transportId, serviceId); if (res) { engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(netId); engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(origNetId); engine->FindObject(*(args.GetAt(3)->GetReference()))->SetVariableValue(transportId); engine->FindObject(*(args.GetAt(4)->GetReference()))->SetVariableValue(serviceId); } SetSuccessFlag(success, res, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("GBI")) // GetBootInfo { // Gets the NB_info field. MHERROR("GetBootInfo ResidentProgram is not implemented"); } else if (m_Name.Equal("CCR")) // CheckContentRef { // Sees if an item with a particular content reference is available // in the carousel. This looks like it should block until the file // is available. The profile recommends that this should be forked // rather than called. if (args.Size() == 3) { MHUnion un; un.GetValueFrom(*(args.GetAt(0)), engine); un.CheckType(MHUnion::U_ContentRef); MHContentRef fileName; fileName.Copy(un.m_ContentRefVal); QString csPath = engine->GetPathName(fileName.m_ContentRef); bool result = false; QByteArray text; // Try to load the object. if (! csPath.isEmpty()) { result = engine->GetContext()->GetCarouselData(csPath, text); } // Set the result variable. MHParameter *pResFlag = args.GetAt(1); engine->FindObject(*(pResFlag->GetReference()))->SetVariableValue(result); MHParameter *pResCR = args.GetAt(2); // Copy the file name to the resulting content ref. engine->FindObject(*(pResCR->GetReference()))->SetVariableValue(fileName); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("CGR")) // CheckGroupIDRef { // Sees if an application or scene with a particular group id // is available in the carousel. MHERROR("CheckGroupIDRef ResidentProgram is not implemented"); } else if (m_Name.Equal("VTG")) // VideoToGraphics { // Video to graphics transformation. MHERROR("VideoToGraphics ResidentProgram is not implemented"); } else if (m_Name.Equal("SWA")) // SetWidescreenAlignment { // Sets either LetterBox or Centre-cut-out mode. // Seems to be concerned with aligning a 4:3 scene with an underlying 16:9 video MHERROR("SetWidescreenAlignment ResidentProgram is not implemented"); } else if (m_Name.Equal("GDA")) // GetDisplayAspectRatio { // Returns the aspcet ratio. 4:3 => 1, 16:9 => 2 MHERROR("GetDisplayAspectRatio ResidentProgram is not implemented"); } else if (m_Name.Equal("CIS")) // CI_SendMessage { // Sends a message to a DVB CI application MHERROR("CI_SendMessage ResidentProgram is not implemented"); } else if (m_Name.Equal("SSM")) // SetSubtitleMode { // Enable or disable subtitles in addition to MHEG. if (args.Size() == 1) { bool status = GetBool(args.GetAt(0), engine); MHLOG(MHLogNotifications, QString("NOTE SetSubtitleMode %1") .arg(status ? "enabled" : "disabled")); // TODO Notify player SetSuccessFlag(success, true, engine); } else SetSuccessFlag(success, false, engine); } else if (m_Name.Equal("WAI")) // WhoAmI { // Return a concatenation of the strings we respond to in // GetEngineSupport(UKEngineProfile(X)) if (args.Size() == 1) { MHOctetString result; result.Copy(engine->MHEGEngineProviderIdString); result.Append(" "); result.Append(engine->GetContext()->GetReceiverId()); result.Append(" "); result.Append(engine->GetContext()->GetDSMCCId()); engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(result); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } } else if (m_Name.Equal("DBG")) // Debug - optional { QString message = "DEBUG: "; for (int i = 0; i < args.Size(); i++) { MHUnion un; un.GetValueFrom(*(args.GetAt(i)), engine); switch (un.m_Type) { case MHUnion::U_Int: message.append(QString("%1").arg(un.m_nIntVal)); break; case MHParameter::P_Bool: message.append(un.m_fBoolVal ? "True" : "False"); break; case MHParameter::P_String: message.append(QString::fromUtf8((const char *)un.m_StrVal.Bytes(), un.m_StrVal.Size())); break; case MHParameter::P_ObjRef: message.append(un.m_ObjRefVal.Printable()); break; case MHParameter::P_ContentRef: message.append(un.m_ContentRefVal.Printable()); break; case MHParameter::P_Null: break; } } MHLOG(MHLogNotifications, message); } else if (m_Name.Equal("SBI")) // SetBroadcastInterrupt { // Required for NativeApplicationExtension // En/dis/able program interruptions e.g. green button if (args.Size() == 1) { bool status = GetBool(args.GetAt(0), engine); MHLOG(MHLogNotifications, QString("NOTE SetBroadcastInterrupt %1") .arg(status ? "enabled" : "disabled")); // Nothing todo at present SetSuccessFlag(success, true, engine); } else SetSuccessFlag(success, false, engine); } // InteractionChannelExtension else if (m_Name.Equal("GIS")) { // GetICStatus if (args.Size() == 1) { int ICstatus = engine->GetContext()->GetICStatus(); MHLOG(MHLogNotifications, "NOTE InteractionChannel " + QString( ICstatus == 0 ? "active" : ICstatus == 1 ? "inactive" : ICstatus == 2 ? "disabled" : "undefined")); engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(ICstatus); SetSuccessFlag(success, true, engine); } else SetSuccessFlag(success, false, engine); } else if (m_Name.Equal("RDa")) { // ReturnData if (args.Size() >= 3) { MHOctetString string; GetString(args.GetAt(0), string, engine); QString url = QString::fromUtf8((const char *)string.Bytes(), string.Size()); // Variable name/value pairs QStringList params; int i = 1; for (; i + 2 < args.Size(); i += 2) { GetString(args.GetAt(i), string, engine); QString name = QString::fromUtf8((const char *)string.Bytes(), string.Size()); QString val; MHUnion un; un.GetValueFrom(*(args.GetAt(i+1)), engine); switch (un.m_Type) { case MHUnion::U_Int: val = QString::number(un.m_nIntVal); break; case MHParameter::P_Bool: val = un.m_fBoolVal ? "true" : "false"; break; case MHParameter::P_String: val = QString::fromUtf8((const char*)un.m_StrVal.Bytes(), un.m_StrVal.Size()); break; case MHParameter::P_ObjRef: val = un.m_ObjRefVal.Printable(); break; case MHParameter::P_ContentRef: val = un.m_ContentRefVal.Printable(); break; case MHParameter::P_Null: val = "<NULL>"; break; default: val = QString("<type %1>").arg(un.m_Type); break; } params += name + "=" + val; } // TODO MHLOG(MHLogNotifications, "NOTE ReturnData '" + url + "' { " + params.join(" ") + " }"); // HTTP response code, 0= none engine->FindObject(*(args.GetAt(i)->GetReference()))->SetVariableValue(0); // HTTP response data string = ""; engine->FindObject(*(args.GetAt(i+1)->GetReference()))->SetVariableValue(string); SetSuccessFlag(success, false, engine); } else SetSuccessFlag(success, false, engine); } else if (m_Name.Equal("SHF")) { // SetHybridFileSystem if (args.Size() == 2) { MHOctetString string; GetString(args.GetAt(0), string, engine); QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size()); GetString(args.GetAt(1), string, engine); QString str2 = QString::fromUtf8((const char *)string.Bytes(), string.Size()); // TODO MHLOG(MHLogNotifications, QString("NOTE SetHybridFileSystem %1=%2") .arg(str).arg(str2)); SetSuccessFlag(success, false, engine); } else SetSuccessFlag(success, false, engine); } else { MHERROR(QString("Unknown ResidentProgram %1").arg(m_Name.Printable())); } } catch (char const *) { // If something went wrong set the succeeded flag to false SetSuccessFlag(success, false, engine); // And continue on. In particular we need to deactivate. } Deactivation(engine); // At the moment we always treat Fork as Call. If we do get a Fork we should signal that we're done. if (fIsFork) { engine->EventTriggered(this, EventAsyncStopped); } }
MHParseNode *MHParseText::DoParse() { MHParseNode *pRes = NULL; try { 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 C_ITEMS: case C_LINK_EFFECT: case C_ACTIVATE: case C_ADD: case C_ADD_ITEM: case C_APPEND: case C_BRING_TO_FRONT: case C_CALL: case C_CALL_ACTION_SLOT: case C_CLEAR: case C_CLONE: case C_CLOSE_CONNECTION: case C_DEACTIVATE: case C_DEL_ITEM: case C_DESELECT: case C_DESELECT_ITEM: case C_DIVIDE: case C_DRAW_ARC: case C_DRAW_LINE: case C_DRAW_OVAL: case C_DRAW_POLYGON: case C_DRAW_POLYLINE: case C_DRAW_RECTANGLE: case C_DRAW_SECTOR: case C_FORK: case C_GET_AVAILABILITY_STATUS: case C_GET_BOX_SIZE: case C_GET_CELL_ITEM: case C_GET_CURSOR_POSITION: case C_GET_ENGINE_SUPPORT: case C_GET_ENTRY_POINT: case C_GET_FILL_COLOUR: case C_GET_FIRST_ITEM: case C_GET_HIGHLIGHT_STATUS: case C_GET_INTERACTION_STATUS: case C_GET_ITEM_STATUS: case C_GET_LABEL: case C_GET_LAST_ANCHOR_FIRED: case C_GET_LINE_COLOUR: case C_GET_LINE_STYLE: case C_GET_LINE_WIDTH: case C_GET_LIST_ITEM: case C_GET_LIST_SIZE: case C_GET_OVERWRITE_MODE: case C_GET_PORTION: case C_GET_POSITION: case C_GET_RUNNING_STATUS: case C_GET_SELECTION_STATUS: case C_GET_SLIDER_VALUE: case C_GET_TEXT_CONTENT: case C_GET_TEXT_DATA: case C_GET_TOKEN_POSITION: case C_GET_VOLUME: case C_LAUNCH: case C_LOCK_SCREEN: case C_MODULO: case C_MOVE: case C_MOVE_TO: case C_MULTIPLY: case C_OPEN_CONNECTION: case C_PRELOAD: case C_PUT_BEFORE: case C_PUT_BEHIND: case C_QUIT: case C_READ_PERSISTENT: case C_RUN: case C_SCALE_BITMAP: case C_SCALE_VIDEO: case C_SCROLL_ITEMS: case C_SELECT: case C_SELECT_ITEM: case C_SEND_EVENT: case C_SEND_TO_BACK: case C_SET_BOX_SIZE: case C_SET_CACHE_PRIORITY: case C_SET_COUNTER_END_POSITION: case C_SET_COUNTER_POSITION: case C_SET_COUNTER_TRIGGER: case C_SET_CURSOR_POSITION: case C_SET_CURSOR_SHAPE: case C_SET_DATA: case C_SET_ENTRY_POINT: case C_SET_FILL_COLOUR: case C_SET_FIRST_ITEM: case C_SET_FONT_REF: case C_SET_HIGHLIGHT_STATUS: case C_SET_INTERACTION_STATUS: case C_SET_LABEL: case C_SET_LINE_COLOUR: case C_SET_LINE_STYLE: case C_SET_LINE_WIDTH: case C_SET_OVERWRITE_MODE: case C_SET_PALETTE_REF: case C_SET_PORTION: case C_SET_POSITION: case C_SET_SLIDER_VALUE: case C_SET_SPEED: case C_SET_TIMER: case C_SET_TRANSPARENCY: case C_SET_VARIABLE: case C_SET_VOLUME: case C_SPAWN: case C_STEP: case C_STOP: case C_STORE_PERSISTENT: case C_SUBTRACT: case C_TEST_VARIABLE: case C_TOGGLE: case C_TOGGLE_ITEM: case C_TRANSITION_TO: case C_UNLOAD: case C_UNLOCK_SCREEN: case C_CONTENT_REFERENCE: case C_TOKEN_GROUP_ITEMS: case C_POSITIONS: case 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 C_ORIGINAL_CONTENT: case C_NEW_GENERIC_BOOLEAN: case C_NEW_GENERIC_INTEGER: case C_NEW_GENERIC_OCTETSTRING: case C_NEW_GENERIC_OBJECT_REF: case C_NEW_GENERIC_CONTENT_REF: case C_ORIGINAL_VALUE: // 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; } case PTInt: { pRes = new MHPInt(m_nInt); NextSym(); break; } case PTBool: { pRes = new MHPBool(m_fBool); NextSym(); break; } case PTString: { MHOctetString str; str.Copy(MHOctetString((const char *)m_String, m_nStringLength)); pRes = new MHPString(str); NextSym(); break; } case PTEnum: { pRes = new MHPEnum(m_nInt); 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"); } return pRes; } catch (...) { delete(pRes); throw; } }