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 {
51  MHIngredient::Initialise(p, engine);
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 
71 void 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 
112 void 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 
121 static 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.
127 static 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.
136 static 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.
145 static 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)
166 static const QDateTime k_mJD_epoch = QDateTime(QDate(1858, 11, 17), QTime(0, 0), Qt::LocalTime);
167 #else
168 static 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
173 static 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 
179 static 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 
186 void 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;
745  case MHUnion::U_String:
746  message.append(QString::fromUtf8((const char *)un.m_strVal.Bytes(), un.m_strVal.Size()));
747  break;
748  case MHUnion::U_ObjRef:
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 
1001 void 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 
1017 void 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 
1043 void 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.
1059  Target(engine)->CallProgram(m_fIsFork, m_succeeded, m_parameters, engine);
1060 }
MHContext::GetCarouselData
virtual bool GetCarouselData(const QString &objectPath, QByteArray &result)=0
MHRoot::Activation
virtual void Activation(MHEngine *engine)
Definition: Root.cpp:70
GetString
static void GetString(MHParameter *parm, MHOctetString &str, MHEngine *engine)
Definition: Programs.cpp:145
MHObjectRef
Definition: BaseClasses.h:153
MHUnion::GetAsString
static const char * GetAsString(enum UnionTypes t)
Definition: BaseClasses.cpp:645
build_compdb.args
args
Definition: build_compdb.py:11
MHObjectRef::Initialise
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseClasses.cpp:263
MHInterChgProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1017
MHProgram::m_name
MHOctetString m_name
Definition: Programs.h:50
GetCurrentDate
static void GetCurrentDate(int64_t &mJDN, int &seconds)
Definition: Programs.cpp:179
MHParameter
Definition: BaseClasses.h:258
MHRoot::Deactivation
virtual void Deactivation(MHEngine *engine)
Definition: Root.cpp:86
MHIngredient::Preparation
void Preparation(MHEngine *engine) override
Definition: Ingredients.cpp:160
MHCall::m_parameters
MHOwnPtrSequence< MHParameter > m_parameters
Definition: Programs.h:101
MHProgram::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:49
MHRoot::m_fAvailable
bool m_fAvailable
Definition: Root.h:252
MHParameter::GetReference
MHObjectRef * GetReference()
Definition: BaseClasses.cpp:754
MHUnion::CheckType
void CheckType(enum UnionTypes t) const
Definition: BaseClasses.cpp:666
Programs.h
MHParameter::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:719
MHEngine::GetContext
MHContext * GetContext()
Definition: Engine.h:154
ASN1Codes.h
MHEngine
Definition: Engine.h:72
MHEngine::MHEGEngineProviderIdString
static const char * MHEGEngineProviderIdString
Definition: Engine.h:158
MHObjectRef::m_groupId
MHOctetString m_groupId
Definition: BaseClasses.h:170
MHObjectRef::m_nObjectNo
int m_nObjectNo
Definition: BaseClasses.h:169
MHIngredient::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Ingredients.cpp:50
MHUnion::m_nIntVal
int m_nIntVal
Definition: BaseClasses.h:302
MHUnion::U_String
@ U_String
Definition: BaseClasses.h:298
MHOctetString::GetAt
unsigned char GetAt(int i) const
Definition: BaseClasses.h:124
MHLogWarning
@ MHLogWarning
Definition: freemheg.h:72
MHOctetString::Bytes
const unsigned char * Bytes() const
Definition: BaseClasses.h:125
MHResidentProgram::CallProgram
void CallProgram(bool fIsFork, const MHObjectRef &success, const MHSequence< MHParameter * > &args, MHEngine *engine) override
Definition: Programs.cpp:186
recoverDateTime
static QDateTime recoverDateTime(int64_t mJDN, int64_t seconds)
Definition: Programs.cpp:173
MHCall::Perform
void Perform(MHEngine *engine) override
Definition: Programs.cpp:1056
MHRoot::SetVariableValue
virtual void SetVariableValue(const MHUnion &)
Definition: Root.h:109
MHElemAction::Initialise
virtual void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseActions.cpp:31
MHSequence< MHParameter * >
MHOctetString
Definition: BaseClasses.h:107
MHEngine::SetTuneInfo
void SetTuneInfo(int tuneinfo)
Definition: Engine.h:165
mythburn.FILE
int FILE
Definition: mythburn.py:138
MHOctetString::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:142
MHContext::TuneTo
virtual bool TuneTo(int channel, int tuneinfo)=0
MHEngine::GetPathName
QString GetPathName(const MHOctetString &str)
Definition: Engine.cpp:534
MHUnion::m_fBoolVal
bool m_fBoolVal
Definition: BaseClasses.h:303
MHCall::PrintArgs
void PrintArgs(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1043
MHCall::m_fIsFork
bool m_fIsFork
Definition: Programs.h:99
MHRoot::m_fRunning
bool m_fRunning
Definition: Root.h:253
MHUnion::U_Int
@ U_Int
Definition: BaseClasses.h:298
MHProgram::m_fInitiallyAvailable
bool m_fInitiallyAvailable
Definition: Programs.h:51
MHEngine::EventTriggered
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:94
MHContext::GetICStatus
virtual int GetICStatus()=0
MHUnion
Definition: BaseClasses.h:283
hardwareprofile.config.p
p
Definition: config.py:33
SetSuccessFlag
static void SetSuccessFlag(const MHObjectRef &success, bool result, MHEngine *engine)
Definition: Programs.cpp:121
ParseNode.h
MHUnion::m_objRefVal
MHObjectRef m_objRefVal
Definition: BaseClasses.h:305
PrintTabs
void PrintTabs(FILE *fd, int n)
Definition: ParseNode.cpp:34
MHParseNode::GetArgN
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
MHResidentProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:112
MHLogDetail
@ MHLogDetail
Definition: freemheg.h:77
MHIngredient::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Ingredients.cpp:108
EventIsRunning
@ EventIsRunning
Definition: Root.h:34
MHEngine::FindObject
MHRoot * FindObject(const MHObjectRef &oRef, bool failOnNotFound=true)
Definition: Engine.cpp:585
MHUnion::U_ContentRef
@ U_ContentRef
Definition: BaseClasses.h:298
MHRemoteProgram::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:995
MHLOG
#define MHLOG(__level, __text)
Definition: Logging.h:36
MHContentRef::Printable
QString Printable() const
Definition: BaseClasses.h:187
MHUnion::U_None
enum MHUnion::UnionTypes U_None
Engine.h
clamp
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
Ingredients.h
MHRemoteProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:1001
MHUnion::GetValueFrom
void GetValueFrom(const MHParameter &value, MHEngine *engine)
Definition: BaseClasses.cpp:615
MHElemAction::Target
MHRoot * Target(MHEngine *engine)
Definition: BaseActions.cpp:46
MHUnion::m_contentRefVal
MHContentRef m_contentRefVal
Definition: BaseClasses.h:306
MHOctetString::Printable
QString Printable() const
Definition: BaseClasses.h:128
MHUnion::U_ObjRef
@ U_ObjRef
Definition: BaseClasses.h:298
MHCall::m_succeeded
MHObjectRef m_succeeded
Definition: Programs.h:100
C_NAME
@ C_NAME
Definition: ASN1Codes.h:100
MHParseNode::GetBoolValue
bool GetBoolValue()
Definition: ParseNode.cpp:192
MHContext::GetDSMCCId
virtual const char * GetDSMCCId(void)=0
Root.h
MHOctetString::Size
int Size() const
Definition: BaseClasses.h:120
MHContentRef
Definition: BaseClasses.h:174
MHCall::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1028
C_INITIALLY_AVAILABLE
@ C_INITIALLY_AVAILABLE
Definition: ASN1Codes.h:101
MHContentRef::m_contentRef
MHOctetString m_contentRef
Definition: BaseClasses.h:189
MHRoot::CallProgram
virtual void CallProgram(bool, const MHObjectRef &, const MHSequence< MHParameter * > &, MHEngine *)
Definition: Root.h:127
MHProgram::Deactivation
void Deactivation(MHEngine *engine) override
Definition: Programs.cpp:101
BaseClasses.h
MHContext::GetServiceInfo
virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId, int &transportId, int &serviceId)=0
MHEngine::GetTuneInfo
int GetTuneInfo()
Definition: Engine.h:164
MHSequence::Size
int Size() const
Definition: BaseClasses.h:47
MHUnion::Printable
QString Printable() const
Definition: BaseClasses.cpp:675
GetBool
static bool GetBool(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:136
MHInterChgProgram::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Programs.cpp:1011
MHParseNode
Definition: ParseNode.h:38
GetInt
static int GetInt(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:127
MHContext::GetChannelIndex
virtual int GetChannelIndex(const QString &str)=0
k_mJD_epoch
static const QDateTime k_mJD_epoch
Midnight on 17 November 1858, the epoch of the modified Julian day.
Definition: Programs.cpp:168
MHUnion::U_Bool
@ U_Bool
Definition: BaseClasses.h:298
MHSequence::GetAt
BASE GetAt(int i) const
Definition: BaseClasses.h:49
MHProgram::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Programs.cpp:71
MHParseNode::GetStringValue
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
MHSequence::Append
void Append(BASE b)
Definition: BaseClasses.h:64
freemheg.h
azlyrics.info
dictionary info
Definition: azlyrics.py:7
MHOctetString::Equal
bool Equal(const MHOctetString &str) const
Definition: BaseClasses.h:123
MHERROR
#define MHERROR(__text)
Definition: Logging.h:42
MHLogNotifications
@ MHLogNotifications
Definition: freemheg.h:73
Logging.h
MHProgram::Activation
void Activation(MHEngine *engine) override
Definition: Programs.cpp:88
MHIngredient::m_fInitiallyActive
bool m_fInitiallyActive
Definition: Ingredients.h:65
MHUnion::m_strVal
MHOctetString m_strVal
Definition: BaseClasses.h:304
MHContext::GetReceiverId
virtual const char * GetReceiverId(void)=0
MHContentRef::Copy
void Copy(const MHContentRef &cr)
Definition: BaseClasses.h:183
MHOctetString::Append
void Append(const MHOctetString &str)
Definition: BaseClasses.cpp:198
MHObjectRef::PrintMe
void PrintMe(FILE *fd, int nTabs) const
Definition: BaseClasses.cpp:284
MHOctetString::Copy
void Copy(const MHOctetString &str)
Definition: BaseClasses.cpp:119
EventAsyncStopped
@ EventAsyncStopped
Definition: Root.h:35
MHObjectRef::Printable
QString Printable() const
Definition: BaseClasses.cpp:298