MythTV master
Programs.cpp
Go to the documentation of this file.
1/* Programs.cpp
2
3 Copyright (C) David C. J. Matthews 2004 dm at prolingua.co.uk
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19
20*/
21#include <algorithm>
22#include <limits>
23#include <random>
24
25#include "Programs.h"
26#include "Ingredients.h"
27#include "Root.h"
28#include "BaseClasses.h"
29#include "ParseNode.h"
30#include "ASN1Codes.h"
31#include "Engine.h"
32#include "Logging.h"
33#include "freemheg.h"
34
35#include <QDateTime>
36#include <QLocale>
37#include <QStringList>
38#include <QTimeZone>
39#include <QUrl>
40#include <QUrlQuery>
41
42/*
43 * Resident programs are subroutines to provide various string and date functions
44 * but also interface to surrounding tuner. They are defined in the UK MHEG profile.
45 * As with many of the more abstruse aspects of MHEG they are not all used in any
46 * applications I've seen so I haven't implemented them.
47 */
48
50{
52 MHParseNode *pCmdNode = p->GetNamedArg(C_NAME);
53
54 if (pCmdNode)
55 {
56 pCmdNode->GetArgN(0)->GetStringValue(m_name); // Program name
57 }
58
59 MHParseNode *pAvail = p->GetNamedArg(C_INITIALLY_AVAILABLE);
60
61 if (pAvail)
62 {
64 }
65
66 // The MHEG Standard says that InitiallyAvailable is mandatory and should be false.
67 // That doesn't seem to be the case in some MHEG programs so we force it here.
68 m_fInitiallyActive = false;
69}
70
71void MHProgram::PrintMe(FILE *fd, int nTabs) const
72{
73 MHIngredient::PrintMe(fd, nTabs);
74 PrintTabs(fd, nTabs);
75 fprintf(fd, ":Name ");
76 m_name.PrintMe(fd, 0);
77 fprintf(fd, "\n");
78
80 {
81 PrintTabs(fd, nTabs);
82 fprintf(fd, ":InitiallyAvailable false");
83 fprintf(fd, "\n");
84 }
85}
86
87// Set "running" and generate an IsRunning event.
89{
90 if (m_fRunning)
91 {
92 return;
93 }
94
96 m_fRunning = true;
97 engine->EventTriggered(this, EventIsRunning);
98}
99
100// This can be called if we change scene while a forked program is running
102{
103 if (! m_fRunning)
104 {
105 return;
106 }
107
108 // TODO: Stop the forked program.
110}
111
112void MHResidentProgram::PrintMe(FILE *fd, int nTabs) const
113{
114 PrintTabs(fd, nTabs);
115 fprintf(fd, "{:ResidentPrg ");
116 MHProgram::PrintMe(fd, nTabs + 1);
117 PrintTabs(fd, nTabs);
118 fprintf(fd, "}\n");
119}
120
121static void SetSuccessFlag(const MHObjectRef &success, bool result, MHEngine *engine)
122{
123 engine->FindObject(success)->SetVariableValue(result);
124}
125
126// Return an integer value. May throw an exception if it isn't the correct type.
127static int GetInt(MHParameter *parm, MHEngine *engine)
128{
129 MHUnion un;
130 un.GetValueFrom(*parm, engine);
132 return un.m_nIntVal;
133}
134
135// Return a bool value. May throw an exception if it isn't the correct type.
136static bool GetBool(MHParameter *parm, MHEngine *engine)
137{
138 MHUnion un;
139 un.GetValueFrom(*parm, engine);
141 return un.m_fBoolVal;
142}
143
144// Extract a string value.
145static void GetString(MHParameter *parm, MHOctetString &str, MHEngine *engine)
146{
147 MHUnion un;
148 un.GetValueFrom(*parm, engine);
150 str.Copy(un.m_strVal);
151}
152
165#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
166static const QDateTime k_mJD_epoch = QDateTime(QDate(1858, 11, 17), QTime(0, 0), Qt::LocalTime);
167#else
168static const QDateTime k_mJD_epoch = QDateTime(QDate(1858, 11, 17), QTime(0, 0),
169 QTimeZone(QTimeZone::LocalTime));
170#endif
171
172// match types with Qt
173static inline QDateTime recoverDateTime(int64_t mJDN, int64_t seconds)
174{
175 QDateTime dt = k_mJD_epoch;
176 return dt.addDays(mJDN).addSecs(seconds);
177}
178
179static void GetCurrentDate(int64_t& mJDN, int& seconds)
180{
181 auto dt = QDateTime::currentDateTime();
182 mJDN = k_mJD_epoch.daysTo(dt); // returns a qint64
183 seconds = dt.time().msecsSinceStartOfDay() / 1000;
184}
185
186void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence<MHParameter *> &args, MHEngine *engine)
187{
188 if (! m_fAvailable)
189 {
190 Preparation(engine);
191 }
192
193 // if (m_fRunning) return; // Strictly speaking there should be only one instance of a program running at a time.
194 Activation(engine);
195 MHLOG(MHLogDetail, QString("Calling program %1").arg(m_name.Printable()));
196
197 try
198 {
199 // Run the code.
200 if (m_name.Equal("GCD")) // GetCurrentDate - returns local time.
201 {
202 if (args.Size() == 2)
203 {
204 int64_t mJDN = 0;
205 int nTimeAsSecs = 0;
206 GetCurrentDate(mJDN, nTimeAsSecs);
207 int nModJulianDate = std::clamp<int64_t>(mJDN, 0, std::numeric_limits<int>::max());
208
209 engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(nModJulianDate);
210 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(nTimeAsSecs);
211 SetSuccessFlag(success, true, engine);
212 }
213 else
214 {
215 SetSuccessFlag(success, false, engine);
216 }
217 }
218
219 else if (m_name.Equal("FDa")) // FormatDate
220 {
221 if (args.Size() == 4)
222 {
223 // This is a bit like strftime but not quite.
224 MHOctetString format;
225 GetString(args.GetAt(0), format, engine);
226 int date = GetInt(args.GetAt(1), engine); // As produced in GCD
227 int time = GetInt(args.GetAt(2), engine);
228
229 QDateTime dt = recoverDateTime(date, time);
230 MHOctetString result;
231
232 for (int i = 0; i < format.Size(); i++)
233 {
234 char ch = format.GetAt(i);
235 QString buffer {};
236
237 if (ch == '%')
238 {
239 i++;
240
241 if (i == format.Size())
242 {
243 break;
244 }
245
246 ch = format.GetAt(i);
247
248 switch (ch)
249 {
250 case 'Y': buffer = dt.toString("yyyy"); break;
251 case 'y': buffer = dt.toString("yy"); break;
252 case 'X': buffer = dt.toString("MM"); break;
253 case 'x': buffer = dt.toString("M"); break;
254 case 'D': buffer = dt.toString("dd"); break;
255 case 'd': buffer = dt.toString("d"); break;
256 case 'H': buffer = dt.toString("HH"); break;
257 case 'h': buffer = dt.toString("H"); break;
258 case 'I':
259 // Need AM/PM to get hours as 1-12
260 buffer = dt.toString("HH AP");
261 buffer.chop(3);
262 break;
263 case 'i':
264 buffer = dt.toString("H AP");
265 buffer.chop(3);
266 break;
267 case 'M': buffer = dt.toString("mm"); break;
268 case 'm': buffer = dt.toString("m"); break;
269 case 'S': buffer = dt.toString("ss"); break;
270 case 's': buffer = dt.toString("s"); break;
271 case 'A': buffer = dt.toString("AP"); break;
272 case 'a': buffer = dt.toString("ap"); break;
273 default:
274 buffer= ch;
275 }
276
277 result.Append(qPrintable(buffer));
278 }
279 else
280 {
281 result.Append(MHOctetString(&ch, 1));
282 }
283 }
284
285 MHParameter *pResString = args.GetAt(3);
286 engine->FindObject(*(pResString->GetReference()))->SetVariableValue(result);
287 SetSuccessFlag(success, true, engine);
288 }
289 else
290 {
291 SetSuccessFlag(success, false, engine);
292 }
293 }
294
295 else if (m_name.Equal("GDW")) // GetDayOfWeek - returns the day of week that the date occurred on.
296 {
297 if (args.Size() == 2)
298 {
299 int date = GetInt(args.GetAt(0), engine); // Date as produced in GCD
300 QDateTime dt = recoverDateTime(date, 0);
301 // ETSI ES 202 184 V2.4.1 (2016-06) §11.10.4.4 GetDayOfWeek
302 // specifies "0 represents Sunday, 1 Monday, etc."
303
304 int dayOfWeek = dt.date().dayOfWeek();
305 // Gregorian calendar, returns 0 if invalid, 1 = Monday to 7 = Sunday
306 if (dayOfWeek == 7)
307 {
308 dayOfWeek = 0;
309 }
310 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(dayOfWeek);
311 SetSuccessFlag(success, true, engine);
312 }
313 else
314 {
315 SetSuccessFlag(success, false, engine);
316 }
317 }
318
319 else if (m_name.Equal("Rnd")) // Random
320 {
321 if (args.Size() == 2)
322 {
323 int nLimit = GetInt(args.GetAt(0), engine);
324 MHParameter *pResInt = args.GetAt(1);
325 // ETSI ES 202 184 V2.4.1 (2016-06) §11.10.5 Random number function
326 // specifies "The returned value is undefined if the num parameter < 1."
327 // so this is fine.
328 static std::random_device rd;
329 static std::mt19937 generator {rd()};
330 std::uniform_int_distribution<int> distrib {0, nLimit};
331 int r = distrib(generator);
332 engine->FindObject(
333 *(pResInt->GetReference()))->SetVariableValue(r);
334 SetSuccessFlag(success, true, engine);
335 }
336 else
337 {
338 SetSuccessFlag(success, false, engine);
339 }
340 }
341
342 else if (m_name.Equal("CTC")) // CastToContentRef
343 {
344 // Converts a string to a ContentRef.
345 if (args.Size() == 2)
346 {
347 MHOctetString string;
348 GetString(args.GetAt(0), string, engine);
349 MHContentRef result;
350 result.m_contentRef.Copy(string);
351 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(result);
352 SetSuccessFlag(success, true, engine);
353 }
354 else
355 {
356 SetSuccessFlag(success, false, engine);
357 }
358 }
359
360 else if (m_name.Equal("CTO")) // CastToObjectRef
361 {
362 // Converts a string and an integer to an ObjectRef.
363 if (args.Size() == 3)
364 {
365 MHObjectRef result;
366 GetString(args.GetAt(0), result.m_groupId, engine);
367 result.m_nObjectNo = GetInt(args.GetAt(1), engine);
368 engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(result);
369 SetSuccessFlag(success, true, engine);
370 }
371 else
372 {
373 SetSuccessFlag(success, false, engine);
374 }
375 }
376
377 else if (m_name.Equal("GSL")) // GetStringLength
378 {
379 if (args.Size() == 2)
380 {
381 // Find a substring within a string and return an index to the position.
382 MHOctetString string;
383 GetString(args.GetAt(0), string, engine);
384 MHParameter *pResInt = args.GetAt(1);
385 SetSuccessFlag(success, true, engine);
386 engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(string.Size());
387 }
388 else
389 {
390 SetSuccessFlag(success, false, engine);
391 }
392 }
393
394 else if (m_name.Equal("GSS")) // GetSubString
395 {
396 if (args.Size() == 4) // Extract a sub-string from a string.
397 {
398 MHOctetString string;
399 GetString(args.GetAt(0), string, engine);
400 int nBeginExtract = GetInt(args.GetAt(1), engine);
401 int nEndExtract = GetInt(args.GetAt(2), engine);
402
403 nBeginExtract = std::clamp(nBeginExtract, 1, string.Size());
404 nEndExtract = std::clamp(nEndExtract, 1, string.Size());
405
406 MHParameter *pResString = args.GetAt(3);
407 // Returns beginExtract to endExtract inclusive.
408 engine->FindObject(*(pResString->GetReference()))->SetVariableValue(
409 MHOctetString(string, nBeginExtract - 1, nEndExtract - nBeginExtract + 1));
410 SetSuccessFlag(success, true, engine);
411 }
412 else
413 {
414 SetSuccessFlag(success, false, engine);
415 }
416 }
417
418 else if (m_name.Equal("SSS")) // SearchSubString
419 {
420 if (args.Size() == 4)
421 {
422 // Find a substring within a string and return an index to the position.
423 MHOctetString string;
424 MHOctetString searchString;
425 GetString(args.GetAt(0), string, engine);
426 int nStart = GetInt(args.GetAt(1), engine);
427
428 nStart = std::max(nStart, 1);
429
430 GetString(args.GetAt(2), searchString, engine);
431 // Strings are indexed from one.
432 int nPos = 0;
433
434 for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++)
435 {
436 int i = 0;
437
438 for (i = 0; i < searchString.Size(); i++)
439 {
440 if (searchString.GetAt(i) != string.GetAt(i + nPos))
441 {
442 break;
443 }
444 }
445
446 if (i == searchString.Size())
447 {
448 break; // Found a match.
449 }
450 }
451
452 // Set the result.
453 MHParameter *pResInt = args.GetAt(3);
454 SetSuccessFlag(success, true, engine); // Set this first.
455
456 if (nPos <= string.Size() - searchString.Size()) // Found
457 {
458 // Set the index to the position of the string, counting from 1.
459 engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1);
460 }
461 else // Not found. Set the result index to -1
462 {
463 engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1);
464 }
465 }
466 else
467 {
468 SetSuccessFlag(success, false, engine);
469 }
470 }
471
472 else if (m_name.Equal("SES")) // SearchAndExtractSubString
473 {
474 if (args.Size() == 5)
475 {
476 // Find a substring within a string and return an index to the position
477 // and the prefix to the substring.
478 MHOctetString string;
479 MHOctetString searchString;
480 GetString(args.GetAt(0), string, engine);
481 int nStart = GetInt(args.GetAt(1), engine);
482
483 nStart = std::max(nStart, 1);
484
485 GetString(args.GetAt(2), searchString, engine);
486 // Strings are indexed from one.
487 int nPos = 0;
488
489 for (nPos = nStart - 1; nPos <= string.Size() - searchString.Size(); nPos++)
490 {
491 int i = 0;
492
493 for (i = 0; i < searchString.Size(); i++)
494 {
495 if (searchString.GetAt(i) != string.GetAt(i + nPos))
496 {
497 break; // Doesn't match
498 }
499 }
500
501 if (i == searchString.Size())
502 {
503 break; // Found a match.
504 }
505 }
506
507 // Set the results.
508 MHParameter *pResString = args.GetAt(3);
509 MHParameter *pResInt = args.GetAt(4);
510 SetSuccessFlag(success, true, engine); // Set this first.
511
512 if (nPos <= string.Size() - searchString.Size()) // Found
513 {
514 // Set the index to the position AFTER the string, counting from 1.
515 engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nPos + 1 + searchString.Size());
516 // Return the sequence from nStart-1 of length nPos-nStart+1
517 MHOctetString resultString(string, nStart - 1, nPos - nStart + 1);
518 engine->FindObject(*(pResString->GetReference()))->SetVariableValue(resultString);
519 }
520 else // Not found. Set the result string to empty and the result index to -1
521 {
522 engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(-1);
523 engine->FindObject(*(pResString->GetReference()))->SetVariableValue(MHOctetString(""));
524 }
525 }
526 else
527 {
528 SetSuccessFlag(success, false, engine);
529 }
530 }
531
532 else if (m_name.Equal("GSI")) // SI_GetServiceIndex
533 {
534 // Returns an index indicating the service
535 if (args.Size() == 2)
536 {
537 MHOctetString string;
538 GetString(args.GetAt(0), string, engine);
539 MHParameter *pResInt = args.GetAt(1);
540 // The format of the service is dvb://netID.[transPortID].serviceID
541 // where the IDs are in hex.
542 // or rec://svc/lcn/N where N is the "logical channel number" i.e. the Freeview channel.
543 QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
544 int nResult = engine->GetContext()->GetChannelIndex(str);
545 engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nResult);
546 SetSuccessFlag(success, nResult >= 0, engine);
547 }
548 else
549 {
550 SetSuccessFlag(success, false, engine);
551 }
552 }
553
554 else if (m_name.Equal("TIn")) // SI_TuneIndex - Fork not allowed
555 {
556 // Tunes to an index returned by GSI
557 if (args.Size() == 1)
558 {
559 int nChannel = GetInt(args.GetAt(0), engine);
560 bool res = nChannel >= 0 ? engine->GetContext()->TuneTo(
561 nChannel, engine->GetTuneInfo()) : false;
562 SetSuccessFlag(success, res, engine);
563 }
564 else
565 {
566 SetSuccessFlag(success, false, engine);
567 }
568 }
569 else if (m_name.Equal("TII")) // SI_TuneIndexInfo
570 {
571 // Indicates whether to perform a subsequent TIn quietly or normally.
572 if (args.Size() == 1)
573 {
574 int tuneinfo = GetInt(args.GetAt(0), engine);
575 engine->SetTuneInfo(tuneinfo);
576 SetSuccessFlag(success, true, engine);
577 }
578 else
579 {
580 SetSuccessFlag(success, false, engine);
581 }
582 }
583 else if (m_name.Equal("BSI")) // SI_GetBasicSI
584 {
585 // Returns basic SI information about the service indicated by an index
586 // returned by GSI.
587 // Returns networkID, origNetworkID, transportStreamID, serviceID
588 if (args.Size() == 5)
589 {
590 int channelId = GetInt(args.GetAt(0), engine);
591 int netId = 0;
592 int origNetId = 0;
593 int transportId = 0;
594 int serviceId = 0;
595 // Look the information up in the database.
596 bool res = engine->GetContext()->GetServiceInfo(channelId, netId, origNetId,
597 transportId, serviceId);
598
599 if (res)
600 {
601 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(netId);
602 engine->FindObject(*(args.GetAt(2)->GetReference()))->SetVariableValue(origNetId);
603 engine->FindObject(*(args.GetAt(3)->GetReference()))->SetVariableValue(transportId);
604 engine->FindObject(*(args.GetAt(4)->GetReference()))->SetVariableValue(serviceId);
605 }
606
607 SetSuccessFlag(success, res, engine);
608 }
609 else
610 {
611 SetSuccessFlag(success, false, engine);
612 }
613 }
614 else if (m_name.Equal("GBI")) // GetBootInfo
615 {
616 // Gets the NB_info field.
617 MHERROR("GetBootInfo ResidentProgram is not implemented");
618 }
619 else if (m_name.Equal("CCR")) // CheckContentRef
620 {
621 // Sees if an item with a particular content reference is available
622 // in the carousel. This looks like it should block until the file
623 // is available. The profile recommends that this should be forked
624 // rather than called.
625 if (args.Size() == 3)
626 {
627 MHUnion un;
628 un.GetValueFrom(*(args.GetAt(0)), engine);
630 MHContentRef fileName;
631 fileName.Copy(un.m_contentRefVal);
632 QString csPath = engine->GetPathName(fileName.m_contentRef);
633 bool result = false;
634 QByteArray text;
635
636 // Try to load the object.
637 if (! csPath.isEmpty())
638 {
639 result = engine->GetContext()->GetCarouselData(csPath, text);
640 }
641
642 // Set the result variable.
643 MHParameter *pResFlag = args.GetAt(1);
644 engine->FindObject(*(pResFlag->GetReference()))->SetVariableValue(result);
645 MHParameter *pResCR = args.GetAt(2);
646 // Copy the file name to the resulting content ref.
647 engine->FindObject(*(pResCR->GetReference()))->SetVariableValue(fileName);
648 SetSuccessFlag(success, true, engine);
649 }
650 else
651 {
652 SetSuccessFlag(success, false, engine);
653 }
654 }
655 else if (m_name.Equal("CGR")) // CheckGroupIDRef
656 {
657 // Sees if an application or scene with a particular group id
658 // is available in the carousel.
659 MHERROR("CheckGroupIDRef ResidentProgram is not implemented");
660 }
661 else if (m_name.Equal("VTG")) // VideoToGraphics
662 {
663 // Video to graphics transformation.
664 MHERROR("VideoToGraphics ResidentProgram is not implemented");
665 }
666 else if (m_name.Equal("SWA")) // SetWidescreenAlignment
667 {
668 // Sets either LetterBox or Centre-cut-out mode.
669 // Seems to be concerned with aligning a 4:3 scene with an underlying 16:9 video
670 MHERROR("SetWidescreenAlignment ResidentProgram is not implemented");
671 }
672 else if (m_name.Equal("GDA")) // GetDisplayAspectRatio
673 {
674 // Returns the aspcet ratio. 4:3 => 1, 16:9 => 2
675 MHERROR("GetDisplayAspectRatio ResidentProgram is not implemented");
676 }
677 else if (m_name.Equal("CIS")) // CI_SendMessage
678 {
679 // Sends a message to a DVB CI application
680 MHERROR("CI_SendMessage ResidentProgram is not implemented");
681 }
682 else if (m_name.Equal("SSM")) // SetSubtitleMode
683 {
684 // Enable or disable subtitles in addition to MHEG.
685 if (args.Size() == 1) {
686 bool status = GetBool(args.GetAt(0), engine);
687 MHLOG(MHLogNotifications, QString("NOTE SetSubtitleMode %1")
688 .arg(status ? "enabled" : "disabled"));
689 // TODO Notify player
690 SetSuccessFlag(success, true, engine);
691 }
692 else
693 {
694 SetSuccessFlag(success, false, engine);
695 }
696 }
697
698 else if (m_name.Equal("WAI")) // WhoAmI
699 {
700 // Return a concatenation of the strings we respond to in
701 // GetEngineSupport(UKEngineProfile(X))
702 if (args.Size() == 1)
703 {
704 MHOctetString result;
705#if 1
706 // BBC Freeview requires a recognized model name which is passed
707 // in a ?whoami=... http query to the interaction channel server.
708 // Otherwise the menu item for iPlayer is not shown
709 result.Copy("SNYPVR");
710#else
711 result.Copy(engine->MHEGEngineProviderIdString);
712 result.Append(" ");
713 result.Append(engine->GetContext()->GetReceiverId());
714 result.Append(" ");
715 result.Append(engine->GetContext()->GetDSMCCId());
716#endif
717 MHLOG(MHLogNotifications, "NOTE WhoAmI -> " + QString::fromUtf8((const char *)result.Bytes(), result.Size()) );
718 engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(result);
719 SetSuccessFlag(success, true, engine);
720 }
721 else
722 {
723 SetSuccessFlag(success, false, engine);
724 }
725 }
726
727 // Optional resident programs
728 else if (m_name.Equal("DBG")) // Debug - optional
729 {
730 QString message = "DEBUG: ";
731
732 for (int i = 0; i < args.Size(); i++)
733 {
734 MHUnion un;
735 un.GetValueFrom(*(args.GetAt(i)), engine);
736
737 switch (un.m_Type)
738 {
739 case MHUnion::U_Int:
740 message.append(QString("%1").arg(un.m_nIntVal));
741 break;
742 case MHUnion::U_Bool:
743 message.append(un.m_fBoolVal ? "True" : "False");
744 break;
746 message.append(QString::fromUtf8((const char *)un.m_strVal.Bytes(), un.m_strVal.Size()));
747 break;
749 message.append(un.m_objRefVal.Printable());
750 break;
752 message.append(un.m_contentRefVal.Printable());
753 break;
754 case MHUnion::U_None:
755 break;
756 }
757 }
758
759 MHLOG(MHLogNotifications, message);
760 }
761
762 // NativeApplicationExtension
763 else if (m_name.Equal("SBI")) // SetBroadcastInterrupt
764 {
765 // Required for NativeApplicationExtension
766 // En/dis/able program interruptions e.g. green button
767 if (args.Size() == 1) {
768 bool status = GetBool(args.GetAt(0), engine);
769 MHLOG(MHLogNotifications, QString("NOTE SetBroadcastInterrupt %1")
770 .arg(status ? "enabled" : "disabled"));
771 // Nothing todo at present
772 SetSuccessFlag(success, true, engine);
773 }
774 else
775 {
776 SetSuccessFlag(success, false, engine);
777 }
778 }
779
780 // InteractionChannelExtension
781 else if (m_name.Equal("GIS")) { // GetICStatus
782 if (args.Size() == 1)
783 {
784 int ICstatus = engine->GetContext()->GetICStatus();
785 MHLOG(MHLogNotifications, "NOTE InteractionChannel " + QString(
786 ICstatus == 0 ? "active" : ICstatus == 1 ? "inactive" :
787 ICstatus == 2 ? "disabled" : "undefined"));
788 engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(ICstatus);
789 SetSuccessFlag(success, true, engine);
790 }
791 else
792 {
793 SetSuccessFlag(success, false, engine);
794 }
795 }
796 else if (m_name.Equal("RDa")) { // ReturnData
797 if (args.Size() >= 3)
798 {
799 MHOctetString string;
800 GetString(args.GetAt(0), string, engine);
801 QUrl url = QString::fromUtf8((const char *)string.Bytes(), string.Size());
802
803 // Variable name/value pairs
804 int i = 1;
805 QUrlQuery q;
806 for (; i + 2 < args.Size(); i += 2)
807 {
808 GetString(args.GetAt(i), string, engine);
809 QString name = QString::fromUtf8((const char *)string.Bytes(), string.Size());
810 MHUnion un;
811 un.GetValueFrom(*(args.GetAt(i+1)), engine);
812 q.addQueryItem(name, un.Printable());
813 }
814 url.setQuery(q);
815
816 MHLOG(MHLogNotifications, QString("NOTE ReturnData %1")
817 .arg(url.toEncoded().constData()) );
818 // NB MHEG-5 says this should be 'post' but 'get; seems to work ok
819 QByteArray text;
820 bool ok = engine->GetContext()->GetCarouselData(url.toEncoded(), text);
821
822 MHLOG(MHLogNotifications, QString("NOTE ReturnData got %1 bytes")
823 .arg(text.size()) );
824
825 // HTTP response code, 0= none
826 engine->FindObject(*(args.GetAt(i)->GetReference()))->SetVariableValue(ok ? 200 : 0);
827 // HTTP response data
828 MHOctetString result;
829 result.Append(text.constData());
830 engine->FindObject(*(args.GetAt(i+1)->GetReference()))->SetVariableValue(result);
831
832 SetSuccessFlag(success, true, engine);
833 }
834 else
835 {
836 SetSuccessFlag(success, false, engine);
837 }
838 }
839 else if (m_name.Equal("SHF")) { // SetHybridFileSystem
840 if (args.Size() == 2)
841 {
842 MHOctetString string;
843 GetString(args.GetAt(0), string, engine);
844 QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
845 GetString(args.GetAt(1), string, engine);
846 QString str2 = QString::fromUtf8((const char *)string.Bytes(), string.Size());
847 // TODO
848 MHLOG(MHLogNotifications, QString("NOTE SetHybridFileSystem %1=%2")
849 .arg(str, str2));
850 SetSuccessFlag(success, false, engine);
851 }
852 else
853 {
854 SetSuccessFlag(success, false, engine);
855 }
856 }
857 else if (m_name.Equal("PST")) { // PersistentStorageInfo
858 if (args.Size() == 1)
859 {
860 engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(true);
861 SetSuccessFlag(success, true, engine);
862 }
863 else
864 {
865 SetSuccessFlag(success, false, engine);
866 }
867 }
868 else if (m_name.Equal("SCk")) { // SetCookie
869 if (args.Size() == 4)
870 {
871 MHOctetString string;
872 GetString(args.GetAt(0), string, engine);
873 QString id = QString::fromUtf8((const char *)string.Bytes(), string.Size());
874 int iExpiry = GetInt(args.GetAt(1), engine);
875 GetString(args.GetAt(2), string, engine);
876 QString val = QString::fromUtf8((const char *)string.Bytes(), string.Size());
877 bool bSecure = GetBool(args.GetAt(3), engine);
878 // TODO
879 MHLOG(MHLogNotifications, QString("NOTE SetCookie id=%1 MJD=%2 value=%3 secure=%4")
880 .arg(id).arg(iExpiry).arg(val).arg(bSecure) );
881 }
882 else
883 {
884 SetSuccessFlag(success, false, engine);
885 }
886 }
887 else if (m_name.Equal("GCk")) { // GetCookie
888 MHERROR("GetCookie ResidentProgram is not implemented");
889 }
890
891 // ICStreamingExtension
892 else if (m_name.Equal("MSP")) // MeasureStreamPerformance
893 {
894 if (args.Size() == 2)
895 {
896 MHOctetString string;
897 GetString(args.GetAt(0), string, engine);
898 QString url = QString::fromUtf8((const char *)string.Bytes(), string.Size());
899 // TODO
900 MHLOG(MHLogNotifications, QString("NOTE MeasureStreamPerformance '%1' %2 bytes")
901 .arg(url).arg(GetInt(args.GetAt(1), engine)) );
902
903 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(-1);
904 SetSuccessFlag(success, true, engine);
905 }
906 else
907 {
908 SetSuccessFlag(success, false, engine);
909 }
910 }
911 else if (m_name.Equal("PFG")) { // PromptForGuidance
912 if (args.Size() == 2)
913 {
914 MHOctetString string;
915 GetString(args.GetAt(0), string, engine);
916 QString info = QString::fromUtf8((const char *)string.Bytes(), string.Size());
917 MHLOG(MHLogNotifications, QString("NOTE PromptForGuidance '%1'").arg(info) );
918
919 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(true);
920 SetSuccessFlag(success, true, engine);
921 }
922 else
923 {
924 SetSuccessFlag(success, false, engine);
925 }
926
927 }
928 else if (m_name.Equal("GAP") || // GetAudioDescPref
929 m_name.Equal("GSP")) { // GetSubtitlePref
930 if (args.Size() == 1)
931 {
932 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(false);
933 SetSuccessFlag(success, true, engine);
934 }
935 else
936 {
937 SetSuccessFlag(success, false, engine);
938 }
939 }
940 else if (m_name.Equal("GPS")) { // GetPINSupport
941 if (args.Size() == 1)
942 {
943 // -1= PIN is not supported
944 // 0= PIN is supported and disabled
945 // 1= PIN is supported and enabled
946 engine->FindObject(*(args.GetAt(1)->GetReference()))->SetVariableValue(0);
947 SetSuccessFlag(success, true, engine);
948 }
949 else
950 {
951 SetSuccessFlag(success, false, engine);
952 }
953 }
954
955 // Undocumented functions
956 else if (m_name.Equal("XBM")) {
957 // BBC Freeview passes 1 boolean arg
958 // Required for BBC Freeview iPlayer
959 MHLOG(MHLogNotifications, "NOTE Undocumented ResidentProgram XBM" );
960 if (args.Size() == 1)
961 engine->FindObject(*(args.GetAt(0)->GetReference()))->SetVariableValue(true);
962 SetSuccessFlag(success, true, engine);
963 }
964
965 else
966 {
967 MHERROR(QString("Unknown ResidentProgram %1").arg(m_name.Printable()));
968 }
969 }
970 catch (...)
971 {
972 QStringList params;
973 for (int i = 0; i < args.Size(); ++i)
974 {
975 MHUnion un;
976 un.GetValueFrom(*(args.GetAt(i)), engine);
977 params += QString(MHUnion::GetAsString(un.m_Type)) + "=" + un.Printable();
978 }
979 MHLOG(MHLogWarning, QString("Arguments (%1)").arg(params.join(",")) );
980
981 // If something went wrong set the succeeded flag to false
982 SetSuccessFlag(success, false, engine);
983 // And continue on. In particular we need to deactivate.
984 }
985
986 Deactivation(engine);
987
988 // At the moment we always treat Fork as Call. If we do get a Fork we should signal that we're done.
989 if (fIsFork)
990 {
991 engine->EventTriggered(this, EventAsyncStopped);
992 }
993}
994
996{
997 MHProgram::Initialise(p, engine);
998 //
999}
1000
1001void MHRemoteProgram::PrintMe(FILE *fd, int nTabs) const
1002{
1003 PrintTabs(fd, nTabs);
1004 fprintf(fd, "{:RemotePrg");
1005 MHProgram::PrintMe(fd, nTabs + 1);
1006 fprintf(fd, "****TODO\n");
1007 PrintTabs(fd, nTabs);
1008 fprintf(fd, "}\n");
1009}
1010
1012{
1013 MHProgram::Initialise(p, engine);
1014 //
1015}
1016
1017void MHInterChgProgram::PrintMe(FILE *fd, int nTabs) const
1018{
1019 PrintTabs(fd, nTabs);
1020 fprintf(fd, "{:InterchgPrg");
1021 MHProgram::PrintMe(fd, nTabs + 1);
1022 fprintf(fd, "****TODO\n");
1023 PrintTabs(fd, nTabs);
1024 fprintf(fd, "}\n");
1025}
1026
1027// Actions.
1029{
1030 MHElemAction::Initialise(p, engine); // Target
1031 m_succeeded.Initialise(p->GetArgN(1), engine); // Call/fork succeeded flag
1032 // Arguments.
1033 MHParseNode *args = p->GetArgN(2);
1034
1035 for (int i = 0; i < args->GetSeqCount(); i++)
1036 {
1037 auto *pParm = new MHParameter;
1038 m_parameters.Append(pParm);
1039 pParm->Initialise(args->GetSeqN(i), engine);
1040 }
1041}
1042
1043void MHCall::PrintArgs(FILE *fd, int nTabs) const
1044{
1045 m_succeeded.PrintMe(fd, nTabs);
1046 fprintf(fd, " ( ");
1047
1048 for (int i = 0; i < m_parameters.Size(); i++)
1049 {
1050 m_parameters.GetAt(i)->PrintMe(fd, 0);
1051 }
1052
1053 fprintf(fd, " )");
1054}
1055
1057{
1058 // Output parameters are handled by IndirectRefs so we don't evaluate the parameters here.
1060}
@ C_INITIALLY_AVAILABLE
Definition: ASN1Codes.h:101
@ C_NAME
Definition: ASN1Codes.h:100
#define MHERROR(__text)
Definition: Logging.h:42
#define MHLOG(__level, __text)
Definition: Logging.h:36
void PrintTabs(FILE *fd, int n)
Definition: ParseNode.cpp:34
static const QDateTime k_mJD_epoch
Midnight on 17 November 1858, the epoch of the modified Julian day.
Definition: Programs.cpp:168
static QDateTime recoverDateTime(int64_t mJDN, int64_t seconds)
Definition: Programs.cpp:173
static void GetCurrentDate(int64_t &mJDN, int &seconds)
Definition: Programs.cpp:179
static int GetInt(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:127
static bool GetBool(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:136
static void SetSuccessFlag(const MHObjectRef &success, bool result, MHEngine *engine)
Definition: Programs.cpp:121
static void GetString(MHParameter *parm, MHOctetString &str, MHEngine *engine)
Definition: Programs.cpp:145
@ EventIsRunning
Definition: Root.h:34
@ EventAsyncStopped
Definition: Root.h:35
MHObjectRef m_succeeded
Definition: Programs.h:100
MHOwnPtrSequence< MHParameter > m_parameters
Definition: Programs.h:101
void PrintArgs(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1043
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1028
void Perform(MHEngine *engine) override
Definition: Programs.cpp:1056
bool m_fIsFork
Definition: Programs.h:99
MHOctetString m_contentRef
Definition: BaseClasses.h:189
void Copy(const MHContentRef &cr)
Definition: BaseClasses.h:183
QString Printable() const
Definition: BaseClasses.h:187
virtual bool TuneTo(int channel, int tuneinfo)=0
virtual const char * GetReceiverId(void)=0
virtual bool GetCarouselData(const QString &objectPath, QByteArray &result)=0
virtual int GetICStatus()=0
virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId, int &transportId, int &serviceId)=0
virtual const char * GetDSMCCId(void)=0
virtual int GetChannelIndex(const QString &str)=0
MHRoot * Target(MHEngine *engine)
Definition: BaseActions.cpp:46
virtual void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseActions.cpp:31
MHRoot * FindObject(const MHObjectRef &oRef, bool failOnNotFound=true)
Definition: Engine.cpp:574
MHContext * GetContext()
Definition: Engine.h:154
QString GetPathName(const MHOctetString &str)
Definition: Engine.cpp:523
static const char * MHEGEngineProviderIdString
Definition: Engine.h:158
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:94
void SetTuneInfo(int tuneinfo)
Definition: Engine.h:165
int GetTuneInfo()
Definition: Engine.h:164
void Preparation(MHEngine *engine) override
bool m_fInitiallyActive
Definition: Ingredients.h:65
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Ingredients.cpp:50
void PrintMe(FILE *fd, int nTabs) const override
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1011
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1017
MHOctetString m_groupId
Definition: BaseClasses.h:170
QString Printable() const
void PrintMe(FILE *fd, int nTabs) const
void Initialise(MHParseNode *p, MHEngine *engine)
void Copy(const MHOctetString &str)
const unsigned char * Bytes() const
Definition: BaseClasses.h:125
int Size() const
Definition: BaseClasses.h:120
unsigned char GetAt(int i) const
Definition: BaseClasses.h:124
void Append(const MHOctetString &str)
void PrintMe(FILE *fd, int nTabs) const
QString Printable() const
Definition: BaseClasses.h:128
bool Equal(const MHOctetString &str) const
Definition: BaseClasses.h:123
MHObjectRef * GetReference()
void PrintMe(FILE *fd, int nTabs) const
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
bool GetBoolValue()
Definition: ParseNode.cpp:192
bool m_fInitiallyAvailable
Definition: Programs.h:51
void Activation(MHEngine *engine) override
Definition: Programs.cpp:88
MHOctetString m_name
Definition: Programs.h:50
void Deactivation(MHEngine *engine) override
Definition: Programs.cpp:101
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:71
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:49
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:995
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1001
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:112
void CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence< MHParameter * > &args, MHEngine *engine) override
Definition: Programs.cpp:186
virtual void CallProgram(bool, const MHObjectRef &, const MHSequence< MHParameter * > &, MHEngine *)
Definition: Root.h:127
virtual void Activation(MHEngine *engine)
Definition: Root.cpp:70
virtual void Deactivation(MHEngine *engine)
Definition: Root.cpp:86
bool m_fRunning
Definition: Root.h:253
bool m_fAvailable
Definition: Root.h:252
virtual void SetVariableValue(const MHUnion &)
Definition: Root.h:109
int Size() const
Definition: BaseClasses.h:47
void Append(BASE b)
Definition: BaseClasses.h:64
BASE GetAt(int i) const
Definition: BaseClasses.h:49
QString Printable() const
bool m_fBoolVal
Definition: BaseClasses.h:303
int m_nIntVal
Definition: BaseClasses.h:302
MHObjectRef m_objRefVal
Definition: BaseClasses.h:305
MHContentRef m_contentRefVal
Definition: BaseClasses.h:306
void GetValueFrom(const MHParameter &value, MHEngine *engine)
void CheckType(enum UnionTypes t) const
@ U_ContentRef
Definition: BaseClasses.h:298
enum MHUnion::UnionTypes U_None
static const char * GetAsString(enum UnionTypes t)
MHOctetString m_strVal
Definition: BaseClasses.h:304
@ MHLogWarning
Definition: freemheg.h:78
@ MHLogNotifications
Definition: freemheg.h:79
@ MHLogDetail
Definition: freemheg.h:83
dictionary info
Definition: azlyrics.py:7
int FILE
Definition: mythburn.py:138
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206