/* ** ReplayTV 5000 ** Guide Parser ** by Lee Thompson Dan Frumin , Todd Larason ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** ** If you distribute a modified version of this program, the program MUST tell the user ** in the usage summary how the program has been modified, and who performed the modification. ** Modified source code MUST be distributed with the modified program. ** ** For additional detail on the Replay 4000/4500/5000 internals, protocols and structures I highly recommend ** visiting Todd's site: http://www.molehill.org/twiki/bin/view/Replay/WebHome ** *************************************************************************************************************** ** ** IMPORTANT NOTE: This is the last version of GuideParser with this codebase. ** ** GuideParser was originally just a research project to learn the Guide Snapshot format. This research ** phase is pretty much at a close and this code is very ugly. ** ** Others wishing to continue development of GuideParser with this code are welcome to do so under the above ** limitations. ** *************************************************************************************************************** ** ** Revision History: ** ** 1.02 - LT Updated for 5.0 Software ** 1.01 - LT ReplayTV 5000 ** 1.0 - LT Final version with this codebase. ** Organized the structures a bit better. ** Fixed some annoying bugs. ** Added -combined option which does a mini replayChannel lookup, best used with -ls ** .11 - LT Whoops! ReplayChannel had the wrong number for szThemeInfo, should've been 64 not 60! (Thanks Clem!) ** Todd corrected a mis-assigned bitmask for TV rating "TV-G" ** Todd's been knocking himself out with research... as a result no more unknowns in tagProgramInfo! ** Added Genre lookups ** .10 - LT Works on FreeBSD now (thanks Andy!) ** Fixed a minor display bug. ** Supports MPAA "G" ratings. ** Added -d option for dumping unknowns as numbers. ** Added -x (Xtra) option for displaying TMSIDs and so forth. ** Added -nowrap to supress word wrap. ** Added STDIN support! ** Added Input Source lookup. ** Better organized sub-structures. ** Channel/Show Output now uses the word wrapper for everything. ** Some minor tweaks. ** Less unknowns. (Thanks in large part to Todd) ** Released on Mar 10th 2002 ** .09 - LT Wrote an int64 Big->Small Endian converter. ** UNIX/WIN32 stuff sorted out. ** Cleaned up some code. ** Dependency on stdafx.h removed. ** Improved Word Wrapper. ** Fixed some bugs. ** Less unknowns. ** Released on Jan 13th 2002 ** .08 - LT More fixes! ** Better wordwrapping! ** Less unknowns! ** Released on Jan 10th 2002 ** .07 - LT Categories :) ** Dan added the extraction command line switches and code. ** Released on Jan 8th 2002 ** .06 - LT Timezone fixes ** Released on Jan 5th 2002 ** .05 - LT Checking to see if guide file is damaged (some versions of ReplayPC have a problem in this area) ** Found a case where GuideHeader.replayshows is not accurate! ** Lots of other fixes. ** Released on Jan 4th 2002 ** .04 - LT Day of Week implemented. ** Categories are loaded but lookup isn't working. ** .03 - LT Private build. ** .02 - LT Private build. ** .01 - LT Private build. ** Initial Version ** 01/03/2002 ** ** *************************************************************************************************************** ** ** TO DO: ** ** 1. Rewrite! ** ** ** BUGS ** Piping from replaypc to guideparser gets a few replaychannels in and somehow loses it's alignment -- looks ** like the 'file' is being truncated. ** *************************************************************************************************************** ** FreeBSD fixes by Andrew Tulloch *************************************************************************************************************** */ #ifdef _XBOX #include #pragma pack(1) // don't use byte alignments on the structs #endif #ifdef _WIN32 #if !defined(AFX_STDAFX_H__11AD8D81_EA54_4CD0_9A74_44F7170DE43F__INCLUDED_) #define AFX_STDAFX_H__11AD8D81_EA54_4CD0_9A74_44F7170DE43F__INCLUDED_ #endif #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #pragma pack(1) // don't use byte alignments on the structs #define WIN32_LEAN_AND_MEAN #if defined (_WIN32) && !defined(_XBOX) #include #endif #include #include #endif #if (defined(__unix__) || defined(__APPLE__)) #include #endif #include #include #include #include #include #include #if defined (_WIN32) && !defined(_XBOX) #pragma comment(lib, "ws2_32.lib") // load winsock2 (we only use it for endian conversion) #define FILEPOS fpos_t #endif #ifdef __APPLE__ #include #endif #if defined(__unix__) || defined(__APPLE__) typedef unsigned char BYTE; typedef unsigned long long DWORD64; typedef unsigned long DWORD; typedef unsigned short WORD; #define FILEPOS unsigned long #define _tzset() tzset() #define _tzname tzname #define _timezone timezone #define MoveMemory(dst,src,len) memmove(dst,src,len) #define HIWORD(x) (WORD)(x >> 16) #endif extern int _daylight; #if defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED > 1040) extern long _timezone; #endif #ifndef _WIN32 extern char *_tzname[2]; #endif #define SHOWSTRUCTURESIZES 1 // this is for debugging... #undef SHOWSTRUCTURESIZES // ...uncomment this line for structure sizes to be shown // 5.0 #define REPLAYSHOW 512 // needed size of replayshow structure #define GUIDEHEADER 840 // needed size of guideheader structure #define PROGRAMINFO 272 // needed size of programinfo structure #define CHANNELINFO 80 // needed size of channelinfo structure // 4.5 #define GUIDEHEADER45 808 // needed size of guideheader structure // 4.3 #define GUIDEHEADER43 808 // needed size of guideheader structure #define REPLAYSHOW43 444 // needed size of replayshow structure #define MAXCHANNELS 99 #define lpszTitle "ReplayTV 5000 (Alpha) GuideParser v1.02 (Build 17)" #define lpszCopyright "(C) 2002 Lee Thompson, Dan Frumin, Todd Larason, and Andrew Tulloch" #define lpszComment "" //------------------------------------------------------------------------- // Data Structures //------------------------------------------------------------------------- //------------------------------------------------------------------------- //******************** COMMENTS ******************************************* //------------------------------------------------------------------------- /* ** General ReplayTV Notes ** ** The RTV4K/5K platforms are capable of supporting NTSC, PAL and SECAM so ** in theory there may be one or more bytes always set a certain way as they ** reflect NTSC. ** ** On the subject of recorded time: The ReplayTV units appear to ** automatically pad shows by 3 seconds. e.g. If you schedule a show that ** airs at 6:00 PM, the recording will actually begin at 5:59:57 PM. ** Because of this, even an unpadded show will have a 3 second discrepency ** between ReplayShow.recorded and ReplayShow.eventtime. ** ** ** Categories ** ** There appears to be an *internal* maximum of 32 but the units appear ** to only use 17 total. ** ** Secondary Offset (GuideHeader) ** ** If you jump to this absolute location this repeats the first 12 bytes of ** this header, null terminates and then 4 unknown DWORDs. ** ** You will then be at the first byte of the first ReplayChannel record. ** ** ** Genre Codes ** Todd Larason's been slowly figuring these out, you can see the list at ** http://www.molehill.org/twiki/bin/view/Replay/RnsGetCG2 ** */ /************************************************************************** ** ** GUIDE HEADER ** ** ** Most of the unknowns in the GuideHeader are garbage/junk. Why ** these aren't initialized to null I have no idea. ** ** They may also be a memory structure that has no meaning on a 'remote unit'. ** **************************************************************************/ // 5000 OS Version 5.0 typedef struct tagGuideSnapshotHeader { WORD osversion; // OS Version (5 for 4.5, 3 for 4.3, 0 on 5.0) WORD snapshotversion; // Snapshot Version (1) (2 on 5.0) This might be better termed as snapshot version DWORD structuresize; // Should always be 64 DWORD unknown1; // 0x00000002 DWORD unknown2; // 0x00000005 DWORD channelcount; // Number of Replay Channels DWORD channelcountcheck; // Should always be equal to channelcount, it's incremented as the snapshot is built. DWORD unknown3; // 0x00000000 DWORD groupdataoffset; // Offset of Group Data DWORD channeloffset; // Offset of First Replay Channel (If you don't care about categories, jump to this) DWORD showoffset; // Offset of First Replay Show (If you don't care about ReplayChannels, jump to this) DWORD snapshotsize; // Total Size of the Snapshot DWORD unknown4; // 0x00000001 DWORD unknown5; // 0x00000004 DWORD flags; // Careful, this is uninitialized, ignore invalid bits DWORD unknown6; // 0x00000002 DWORD unknown7; // 0x00000000 } GuideSnapshotHeader; typedef struct tagGroupData { DWORD structuresize; // Should always be 776 DWORD categories; // Number of Categories (MAX: 32 [ 0 - 31 ] ) DWORD category[32]; // category lookup 2 ^ number, position order = text DWORD categoryoffset[32]; // Offsets for the GuideHeader.catbuffer char catbuffer[512]; // GuideHeader.categoryoffset contains the starting position of each category. } GroupData; typedef struct tagGuideHeader { struct tagGuideSnapshotHeader GuideSnapshotHeader; struct tagGroupData GroupData; } GuideHeader; // 4000 OS Version 4.3 / 5000 OS Version 4.5 typedef struct tagGuideSnapshotHeader45 { WORD osversion; // Major Revision (5 for Replay5000s, 3 for Replay 4000/4500s) WORD snapshotversion; // Minor Revision (1) DWORD structuresize; // Should always be 32 DWORD channelcount; // Number of Replay Channels DWORD channelcountcheck; // Number of Replay Channels (Copy 2; should always match) DWORD groupdataoffset; // Offset of Group Data DWORD channeloffset; // Offset of First Replay Channel (If you don't care about categories, jump to this) DWORD showoffset; // Offset of First Replay Show (If you don't care about ReplayChannels, jump to this) DWORD flags; // Careful, this is uninitialized, ignore invalid bits } GuideSnapshotHeader45; // GroupData structure is the same for 4.3/4.5 and 5.0 typedef struct tagGuideHeader45 { struct tagGuideSnapshotHeader45 GuideSnapshotHeader; struct tagGroupData GroupData; } GuideHeader45; /******************************************************************************* ** ** SUB-STRUCTURES ** *******************************************************************************/ //------------------------------------------------------------------------- // Movie Extended Info Structure typedef struct tagMovieInfo { WORD mpaa; // MPAA Rating WORD stars; // Star Rating * 10 (i.e. value of 20 = 2 stars) WORD year; // Release Year WORD runtime; // Strange HH:MM format } MovieInfo; //------------------------------------------------------------------------- // Has Parts Info Structure typedef struct tagPartsInfo { WORD partnumber; // Part X [Of Y] WORD maxparts; // [Part X] Of Y } PartsInfo; //-------------------------------------------------------------------------- // Program Info Structure typedef struct tagProgramInfo { DWORD structuresize; // Should always be 272 (0x0110) DWORD autorecord; // If non-zero it is an automatic recording, otherwise it is a manual recording. DWORD isvalid; // Not sure what it actually means! (should always be 1 in exported guide) DWORD tuning; // Tuning (Channel Number) for the Program DWORD flags; // Program Flags DWORD eventtime; // Scheduled Time of the Show DWORD tmsID; // Tribune Media Services ID (inherited from ChannelInfo) WORD minutes; // Minutes (add with show padding for total) BYTE genre1; // Genre Code 1 BYTE genre2; // Genre Code 2 BYTE genre3; // Genre Code 3 BYTE genre4; // Genre Code 4 WORD recLen; // Record Length of Description Block BYTE titleLen; // Length of Title BYTE episodeLen; // Length of Episode BYTE descriptionLen; // Length of Description BYTE actorLen; // Length of Actors BYTE guestLen; // Length of Guest BYTE suzukiLen; // Length of Suzuki String (Newer genre tags) BYTE producerLen; // Length of Producer BYTE directorLen; // Length of Director char szDescription[228]; // This can have parts/movie sub-structure } ProgramInfo; //-------------------------------------------------------------------------- // Channel Info Structure typedef struct tagChannelInfo { DWORD structuresize; // Should always be 80 (0x50) DWORD usetuner; // If non-zero the tuner is used, otherwise it's a direct input. DWORD isvalid; // Record valid if non-zero DWORD tmsID; // Tribune Media Services ID WORD channel; // Channel Number BYTE device; // Device BYTE tier; // Cable/Satellite Service Tier char szChannelName[16]; // Channel Name char szChannelLabel[32]; // Channel Description char cablesystem[8]; // Cable system ID DWORD channelindex; // Channel Index (USUALLY tagChannelinfo.channel repeated) } ChannelInfo; /******************************************************************************* ** ** REPLAY SHOW ** *******************************************************************************/ typedef struct tagReplayShow { DWORD created; // ReplayChannel ID (tagReplayChannel.created) DWORD recorded; // Filename/Time of Recording (aka ShowID) DWORD inputsource; // Replay Input Source DWORD quality; // Recording Quality Level DWORD guaranteed; // (0xFFFFFFFF if guaranteed) DWORD playbackflags; // Not well understood yet. struct tagChannelInfo ChannelInfo; struct tagProgramInfo ProgramInfo; DWORD ivsstatus; // Always 1 in a snapshot outside of a tagReplayChannel DWORD guideid; // The show_id on the original ReplayTV for IVS shows. Otherwise 0 DWORD downloadid; // Valid only during actual transfer of index or mpeg file; format/meaning still unknown DWORD timessent; // Times sent using IVS DWORD seconds; // Show Duration in Seconds (this is the exact actual length of the recording) DWORD GOP_count; // MPEG Group of Picture Count DWORD GOP_highest; // Highest GOP number seen, 0 on a snapshot. DWORD GOP_last; // Last GOP number seen, 0 on a snapshot. DWORD checkpointed; // 0 in possibly-out-of-date in-memory copies; always -1 in snapshots DWORD intact; // 0xffffffff in a snapshot; 0 means a deleted show DWORD upgradeflag; // Always 0 in a snapshot DWORD instance; // Episode Instance Counter (0 offset) WORD unused; // Not preserved when padding values are set, presumably not used BYTE beforepadding; // Before Show Padding BYTE afterpadding; // After Show Padding DWORD64 indexsize; // Size of NDX file (IVS Shows Only) DWORD64 mpegsize; // Size of MPG file (IVS Shows Only) char szReserved[68]; // Show Label (NOT PRESENT in 4k 4.3 version, work around it) } ReplayShow; //------------------------------------------------------------------------- // WIN32 Functions //------------------------------------------------------------------------- #ifdef _WIN32 //------------------------------------------------------------------------- void UnixTimeToFileTime(time_t t, LPFILETIME pft) { LONGLONG ll; ll = Int32x32To64(t, 10000000) + 116444736000000000; pft->dwLowDateTime = (DWORD)ll; pft->dwHighDateTime = (DWORD)(ll >> 32); } //------------------------------------------------------------------------- void UnixTimeToSystemTime(time_t t, LPSYSTEMTIME pst) { FILETIME ft; UnixTimeToFileTime(t, &ft); FileTimeToSystemTime(&ft, pst); } //------------------------------------------------------------------------- char * UnixTimeToString(time_t t) { static char sbuf[17]; char szTimeZone[32]; SYSTEMTIME st; int tzbias; _tzset(); memset(szTimeZone,0,sizeof(szTimeZone)); MoveMemory(szTimeZone,_tzname[0],1); strcat(szTimeZone,"T"); #if (MAC_OS_X_VERSION_MAX_ALLOWED <= 1040) && !defined(_WIN32) struct timezone tz; gettimeofday(NULL, &tz); tzbias = tz.tz_minuteswest; #else tzbias = _timezone; #endif UnixTimeToSystemTime(t - tzbias, &st); sprintf(sbuf, "%d-%.2d-%.2d %.2d:%.2d:%.2d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); return sbuf; } #endif //------------------------------------------------------------------------- // UNIX Functions //------------------------------------------------------------------------- #if defined(__unix__) || defined(__APPLE__) char * UnixTimeToString(time_t t) { static char sbuf[17]; struct tm * tm; tm = localtime(&t); strftime(sbuf, sizeof(sbuf), "%Y-%m-%d %H:%M:%S", tm); return sbuf; } #endif //------------------------------------------------------------------------- // Common Functions //------------------------------------------------------------------------- //------------------------------------------------------------------------- // Lookup Tables //------------------------------------------------------------------------- //------------------------------------------------------------------------- void DisplayQuality(int quality, char *szQuality) { switch( quality ) { case 0: strcpy( szQuality, "High" ); break; case 1: strcpy( szQuality, "Medium" ); break; case 2: strcpy( szQuality, "Standard" ); break; default: strcpy( szQuality, "" ); break; } } //------------------------------------------------------------------------- void DisplayExtendedTVRating(int tvrating, char *szRating, bool append) { if (append == false) { strcpy( szRating, ""); } if (tvrating & 0x00020000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "S" ); // Sex } if (tvrating & 0x00040000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "V" ); // Violence } if (tvrating & 0x00080000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "L" ); // Language } if (tvrating & 0x00100000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "D" ); // Drug Use } if (tvrating & 0x00200000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "F" ); // Fantasy Violence } } //------------------------------------------------------------------------- void DisplayTVRating(int tvrating, char *szRating) { strcpy( szRating, ""); if (tvrating & 0x00008000) { strcpy( szRating, "TV-Y" ); } if (tvrating & 0x00010000) { strcpy( szRating, "TV-Y7" ); } if (tvrating & 0x00001000) { strcpy( szRating, "TV-G" ); } if (tvrating & 0x00004000) { strcpy( szRating, "TV-PG" ); } if (tvrating & 0x00002000) { strcpy( szRating, "TV-MA" ); } if (tvrating & 0x00000800) { strcpy( szRating, "TV-14" ); } } //------------------------------------------------------------------------- void DisplayExtendedMPAARating(int mpaarating, char *szRating, bool append) { if (append == false) { strcpy( szRating, ""); } if (mpaarating & 0x00400000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "AC" ); // Adult Content } if (mpaarating & 0x00800000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "BN" ); // Brief Nudity } if (mpaarating & 0x01000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "GL" ); // Graphic Language } if (mpaarating & 0x02000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "GV" ); // Graphic Violence } if (mpaarating & 0x04000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "AL" ); // Adult Language } if (mpaarating & 0x08000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "MV" ); // Mild Violence } if (mpaarating & 0x10000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "N" ); // Nudity } if (mpaarating & 0x20000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "RP" ); // Rape } if (mpaarating & 0x40000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "SC " ); // Sexual Content } if (mpaarating & 0x80000000) { if (strlen(szRating) > 0) { strcat( szRating, " "); } strcat( szRating, "V" ); // Violence } } //------------------------------------------------------------------------- void DisplayMPAARating(int mpaarating, char *szRating) { strcpy( szRating, ""); if (mpaarating & 0x00000001) { strcat( szRating, "AO" ); // unverified } if (mpaarating & 0x00000002) { strcat( szRating, "G" ); } if (mpaarating & 0x00000004) { strcat( szRating, "NC-17" ); } if (mpaarating & 0x00000008) { strcat( szRating, "NR" ); } if (mpaarating & 0x00000010) { strcat( szRating, "PG" ); } if (mpaarating & 0x00000020) { strcat( szRating, "PG-13" ); } if (mpaarating & 0x00000040) { strcat( szRating, "R" ); } } //------------------------------------------------------------------------- // Data Presentation Functions //------------------------------------------------------------------------- //------------------------------------------------------------------------- void ConvertCodepage(char *szString) { unsigned int i = 0; char ch; if (szString[0] == 0) { return; } for( i = 0; i < strlen(szString); ++i ) { ch = szString[i]; // Windows Codepage Translation to DOS/UNIX etc if (ch == (char)146) { szString[i] = '\''; } if (ch == (char)147) { szString[i] = '\"'; } if (ch == (char)148) { szString[i] = '\"'; } #ifdef _WIN32 // Windows (Console) specific translations #endif #if defined(__unix__) || defined(__APPLE__) // UNIX specific translations #endif } return; } //------------------------------------------------------------------------- // Misc. Functions //------------------------------------------------------------------------- //------------------------------------------------------------------------- int CalculateMinutes( int seconds ) { int retval = 0; double result1 = 0.0; double result2 = 0.0; if (seconds < 1) { return 0; } result1 = seconds / 60; result2 = seconds % 60; if (result2 > .0) // this will round up if there's any remainder, // could also make this .5 etc { retval = (int)result1 + 1; } else { retval = (int)result1; } return retval; } //------------------------------------------------------------------------- // ENDIAN CONVERSION FUNCTIONS //------------------------------------------------------------------------- //------------------------------------------------------------------------- // LONGLONG/INT64 Endian Conversion //------------------------------------------------------------------------- //------------------------------------------------------------------------- #if _MSC_VER < 1700 DWORD64 ntohll(DWORD64 llValue) { DWORD64 retval = 0; // This is really cheesy but it works so that's all that matters // If someone out there wants to replace this with something cooler, // please feel free to do so! char szBuffer[17]; char szInt64[17]; memset(szBuffer,0,sizeof(szBuffer)); memset(szInt64,0,sizeof(szInt64)); MoveMemory(szBuffer,&llValue,sizeof(llValue)); size_t cc = sizeof(llValue) - 1; size_t i = 0; for( i = 0; i < sizeof(llValue); ++i ) { szInt64[i] = szBuffer[cc]; --cc; } MoveMemory(&retval,szInt64,sizeof(retval)); return retval; } #endif // _MSC_VER < 1700 //------------------------------------------------------------------------- void ConvertProgramInfoEndian(struct tagProgramInfo * strProgramInfo) { strProgramInfo->autorecord = ntohl(strProgramInfo->autorecord); strProgramInfo->eventtime = ntohl(strProgramInfo->eventtime); strProgramInfo->flags = ntohl(strProgramInfo->flags); strProgramInfo->isvalid = ntohl(strProgramInfo->isvalid); strProgramInfo->minutes = ntohs(strProgramInfo->minutes); strProgramInfo->recLen = ntohs(strProgramInfo->recLen); strProgramInfo->structuresize = ntohl(strProgramInfo->structuresize); strProgramInfo->tuning = ntohl(strProgramInfo->tuning); strProgramInfo->tmsID = ntohl(strProgramInfo->tmsID); return; } //------------------------------------------------------------------------- void ConvertMovieInfoEndian(struct tagMovieInfo * strMovieInfo) { strMovieInfo->mpaa = ntohs(strMovieInfo->mpaa); strMovieInfo->runtime = ntohs(strMovieInfo->runtime); strMovieInfo->stars = ntohs(strMovieInfo->stars); strMovieInfo->year = ntohs(strMovieInfo->year); return; } //------------------------------------------------------------------------- void ConvertPartsInfoEndian(struct tagPartsInfo * strPartsInfo) { strPartsInfo->maxparts = ntohs(strPartsInfo->maxparts); strPartsInfo->partnumber = ntohs(strPartsInfo->partnumber); return; } //------------------------------------------------------------------------- void ConvertReplayShowEndian(struct tagReplayShow * strReplayShow) { strReplayShow->checkpointed = ntohl(strReplayShow->checkpointed); strReplayShow->created = ntohl(strReplayShow->created); strReplayShow->downloadid = ntohl(strReplayShow->downloadid); strReplayShow->GOP_count = ntohl(strReplayShow->GOP_count); strReplayShow->GOP_highest = ntohl(strReplayShow->GOP_highest); strReplayShow->GOP_last = ntohl(strReplayShow->GOP_last); strReplayShow->guaranteed = ntohl(strReplayShow->guaranteed); strReplayShow->guideid = ntohl(strReplayShow->guideid); strReplayShow->indexsize = ntohll(strReplayShow->indexsize); strReplayShow->inputsource = ntohl(strReplayShow->inputsource); strReplayShow->instance = ntohl(strReplayShow->instance); strReplayShow->intact = ntohl(strReplayShow->intact); strReplayShow->ivsstatus = ntohl(strReplayShow->ivsstatus); strReplayShow->mpegsize = ntohll(strReplayShow->mpegsize); strReplayShow->playbackflags = ntohl(strReplayShow->playbackflags); strReplayShow->quality = ntohl(strReplayShow->quality); strReplayShow->recorded = ntohl(strReplayShow->recorded); strReplayShow->seconds = ntohl(strReplayShow->seconds); strReplayShow->timessent = ntohl(strReplayShow->timessent); strReplayShow->upgradeflag = ntohl(strReplayShow->upgradeflag); strReplayShow->unused = ntohs(strReplayShow->unused); return; } //------------------------------------------------------------------------- void ConvertReplayGuideEndian(struct tagGuideHeader * strGuideHeader) { strGuideHeader->GroupData.categories = ntohl(strGuideHeader->GroupData.categories); strGuideHeader->GroupData.structuresize = ntohl(strGuideHeader->GroupData.structuresize); strGuideHeader->GuideSnapshotHeader.snapshotsize = ntohl(strGuideHeader->GuideSnapshotHeader.snapshotsize); strGuideHeader->GuideSnapshotHeader.channeloffset = ntohl(strGuideHeader->GuideSnapshotHeader.channeloffset); strGuideHeader->GuideSnapshotHeader.flags = ntohl(strGuideHeader->GuideSnapshotHeader.flags); strGuideHeader->GuideSnapshotHeader.groupdataoffset = ntohl(strGuideHeader->GuideSnapshotHeader.groupdataoffset); strGuideHeader->GuideSnapshotHeader.osversion = ntohs(strGuideHeader->GuideSnapshotHeader.osversion); strGuideHeader->GuideSnapshotHeader.snapshotversion = ntohs(strGuideHeader->GuideSnapshotHeader.snapshotversion); strGuideHeader->GuideSnapshotHeader.channelcount = ntohl(strGuideHeader->GuideSnapshotHeader.channelcount); strGuideHeader->GuideSnapshotHeader.channelcountcheck = ntohl(strGuideHeader->GuideSnapshotHeader.channelcountcheck); strGuideHeader->GuideSnapshotHeader.showoffset = ntohl(strGuideHeader->GuideSnapshotHeader.showoffset); strGuideHeader->GuideSnapshotHeader.unknown1 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown1); strGuideHeader->GuideSnapshotHeader.unknown2 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown2); strGuideHeader->GuideSnapshotHeader.unknown3 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown3); strGuideHeader->GuideSnapshotHeader.unknown4 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown4); strGuideHeader->GuideSnapshotHeader.unknown5 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown5); strGuideHeader->GuideSnapshotHeader.unknown6 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown6); strGuideHeader->GuideSnapshotHeader.unknown7 = ntohl(strGuideHeader->GuideSnapshotHeader.unknown7); for( unsigned int cc = 0; cc < strGuideHeader->GroupData.categories; ++cc ) { strGuideHeader->GroupData.category[cc] = ntohl(strGuideHeader->GroupData.category[cc]); strGuideHeader->GroupData.categoryoffset[cc] = ntohl(strGuideHeader->GroupData.categoryoffset[cc]); } { strGuideHeader->GuideSnapshotHeader.structuresize = ntohl(strGuideHeader->GuideSnapshotHeader.structuresize); } return; } //------------------------------------------------------------------------- void ConvertReplayGuide45To50Endian(struct tagGuideHeader45 * strGuideHeader45, struct tagGuideHeader * strGuideHeader) { strGuideHeader->GroupData.categories = ntohl(strGuideHeader45->GroupData.categories); strGuideHeader->GroupData.structuresize = ntohl(strGuideHeader45->GroupData.structuresize); strGuideHeader->GuideSnapshotHeader.snapshotsize = 0; strGuideHeader->GuideSnapshotHeader.channeloffset = ntohl(strGuideHeader45->GuideSnapshotHeader.channeloffset); strGuideHeader->GuideSnapshotHeader.flags = ntohl(strGuideHeader45->GuideSnapshotHeader.flags); strGuideHeader->GuideSnapshotHeader.groupdataoffset = ntohl(strGuideHeader45->GuideSnapshotHeader.groupdataoffset); strGuideHeader->GuideSnapshotHeader.osversion = ntohs(strGuideHeader45->GuideSnapshotHeader.osversion); strGuideHeader->GuideSnapshotHeader.snapshotversion = ntohs(strGuideHeader45->GuideSnapshotHeader.snapshotversion); strGuideHeader->GuideSnapshotHeader.channelcount = ntohl(strGuideHeader45->GuideSnapshotHeader.channelcount); strGuideHeader->GuideSnapshotHeader.channelcountcheck = ntohl(strGuideHeader45->GuideSnapshotHeader.channelcountcheck); strGuideHeader->GuideSnapshotHeader.showoffset = ntohl(strGuideHeader45->GuideSnapshotHeader.showoffset); strGuideHeader->GuideSnapshotHeader.unknown1 = 0; strGuideHeader->GuideSnapshotHeader.unknown2 = 0; strGuideHeader->GuideSnapshotHeader.unknown3 = 0; strGuideHeader->GuideSnapshotHeader.unknown4 = 0; strGuideHeader->GuideSnapshotHeader.unknown5 = 0; strGuideHeader->GuideSnapshotHeader.unknown6 = 0; strGuideHeader->GuideSnapshotHeader.unknown7 = 0; for( unsigned int cc = 0; cc < strGuideHeader->GroupData.categories; ++cc ) { strGuideHeader->GroupData.category[cc] = ntohl(strGuideHeader45->GroupData.category[cc]); strGuideHeader->GroupData.categoryoffset[cc] = ntohl(strGuideHeader45->GroupData.categoryoffset[cc]); } { strGuideHeader->GuideSnapshotHeader.structuresize = ntohl(strGuideHeader45->GuideSnapshotHeader.structuresize); } return; } //------------------------------------------------------------------------- // GuideParser //------------------------------------------------------------------------- extern "C" { int GuideParser(char * szOutputBuffer, const char * szInput, const size_t InputSize) { const char * fptr, * junkptr; bool moreshows, receivedshow; bool isguaranteed, isrepeat, isletterbox, ismovie, hasparts, iscc, isppv, isstereo, issap; char szBuffer[1024], szQuality[16]; char szEpisodeTitle[128]; char szStarRating[6], szMPAARating[10], szOriginalDescriptionBuffer[226]; char szTVRating[32]; char szShowTitle[128]; char szExtTV[64]; char szExtMPAA[64]; int i, curshow, cc; size_t readsize; ReplayShow strReplayShow; GuideHeader strGuideHeader; GuideHeader45 strGuideHeader45; MovieInfo strMovieInfo; PartsInfo strPartsInfo; // Initialize memset(szBuffer,0,sizeof(szBuffer)); memset(szQuality,0,sizeof(szQuality)); memset(szEpisodeTitle,0,sizeof(szEpisodeTitle)); memset(szOriginalDescriptionBuffer,0,sizeof(szOriginalDescriptionBuffer)); memset(szStarRating,0,sizeof(szStarRating)); memset(szMPAARating,0,sizeof(szMPAARating)); memset(szExtTV,0,sizeof(szExtTV)); memset(szExtMPAA,0,sizeof(szExtMPAA)); sprintf(szOutputBuffer,"%s", ""); // Display Initial Header //printf("%s\n",lpszTitle); //printf("%s\n\n",lpszCopyright); //printf("%s\n",lpszComment); // Structure Sizes #ifdef SHOWSTRUCTURESIZES //printf("struct strGuideHeader is %d bytes\n",sizeof(tagGuideHeader)); //printf("struct strMovieInfo is %d bytes\n",sizeof(tagMovieInfo)); //printf("struct strPartsInfo is %d bytes\n",sizeof(tagPartsInfo)); //printf("struct strProgramInfo is %d bytes\n",sizeof(tagProgramInfo)); //printf("struct strChannelInfo is %d bytes\n",sizeof(tagChannelInfo)); //printf("struct strReplayShow is %d bytes\n",sizeof(tagReplayShow)); //printf("\n"); #endif #ifdef _DEBUG // Check to make sure structures are required sizes, this is mostly for those of you tinkering with this // and - of course - me :). if (sizeof(ProgramInfo) != PROGRAMINFO) { //printf("Error in ProgramInfo structure, needs to be %u instead of %u\n",PROGRAMINFO,sizeof(ProgramInfo)); } if (sizeof(ChannelInfo) != CHANNELINFO) { //printf("Error in ChannelInfo structure, needs to be %u instead of %u\n",CHANNELINFO,sizeof(ChannelInfo)); } if (sizeof(ReplayShow) != REPLAYSHOW) { //printf("Error in ReplayShow structure, needs to be %u instead of %u\n",REPLAYSHOW,sizeof(ReplayShow)); } if (sizeof(GuideHeader) != GUIDEHEADER) { //printf("Error in GuideHeader structure, needs to be %u instead of %u\n",GUIDEHEADER,sizeof(GuideHeader)); } // 4.5 if (sizeof(GuideHeader45) != GUIDEHEADER45) { //printf("Error in GuideHeader45 structure, needs to be %u instead of %u\n",GUIDEHEADER45,sizeof(GuideHeader45)); } #endif curshow = 0; i = 0; junkptr = szInput; fptr = szInput; DWORD ver; memcpy(&ver, fptr, sizeof(ver)); ver = ntohl(ver); //printf("V %d.%d!\n", HIWORD(ver), LOWORD(ver)); //------------------------------------------------------------------------- // Replay Header //------------------------------------------------------------------------- //printf("Reading ReplayTV Guide Header\n"); switch(HIWORD(ver)) { case 0: // 5k 5.0 memcpy( &strGuideHeader, fptr, sizeof(strGuideHeader) ); ConvertReplayGuideEndian(&strGuideHeader); break; case 3: // 4k 4.3 case 5: // 5k 4.5 memcpy( &strGuideHeader45, fptr, sizeof(strGuideHeader45) ); ConvertReplayGuide45To50Endian(&strGuideHeader45, &strGuideHeader); break; default: // Unknown: try 5k 5.0 //printf("Unknown ReplayTV version: %d.%d!\n", HIWORD(ver), LOWORD(ver)); memcpy( &strGuideHeader, fptr, sizeof(strGuideHeader) ); ConvertReplayGuideEndian(&strGuideHeader); break; } if (strGuideHeader.GuideSnapshotHeader.channelcount != strGuideHeader.GuideSnapshotHeader.channelcountcheck) { //printf("Guide Snapshot might be damaged. (%u/%u)\n",strGuideHeader.GuideSnapshotHeader.channelcount,strGuideHeader.GuideSnapshotHeader.channelcountcheck); } // jump to the first show record // fptr = (long)strGuideHeader.GuideSnapshotHeader.showoffset + junkptr; fptr = (long)strGuideHeader.GuideSnapshotHeader.showoffset + junkptr; //------------------------------------------------------------------------- // ReplayShows //------------------------------------------------------------------------- //printf("\n\nReading ReplayShows\n\n"); moreshows = true; curshow = 0; strcat(szOutputBuffer, "\n"); do { readsize = sizeof(strReplayShow); // 4k 4.3 doesn't have the extra bytes for szReserved at the end of the struct if ( HIWORD( ver ) == 3 ) readsize -= sizeof(strReplayShow.szReserved); // Need to check if ptr is at, beyond or near end of input (i.e. within sizeof(strReplayshow)) if ( (InputSize - (fptr - szInput) ) < readsize) { moreshows = false; } else { memcpy( &strReplayShow, fptr, readsize); curshow++; memset(szTVRating,0,sizeof(szTVRating)); memset(szBuffer,0,sizeof(szBuffer)); memset(szEpisodeTitle,0,sizeof(szEpisodeTitle)); memset(szShowTitle,0,sizeof(szShowTitle)); memset(szMPAARating,0,sizeof(szMPAARating)); memset(szOriginalDescriptionBuffer,0,sizeof(szOriginalDescriptionBuffer)); strcpy(szStarRating,"*****"); memset(szExtTV,0,sizeof(szExtTV)); memset(szExtMPAA,0,sizeof(szExtMPAA)); // Convert Endian ConvertReplayShowEndian(&strReplayShow); ConvertProgramInfoEndian(&strReplayShow.ProgramInfo); /* // this is for this hack, this seems to be no longer used so copy it from eventtime if (strReplayShow.recorded == 0) { strReplayShow.recorded = strReplayShow.ProgramInfo.eventtime; } */ // Process ProgramInfo MoveMemory(szOriginalDescriptionBuffer,strReplayShow.ProgramInfo.szDescription,sizeof(szOriginalDescriptionBuffer)); // Process Flags iscc = false; issap = false; isstereo = false; ismovie = false; isppv = false; hasparts = false; isletterbox = false; isrepeat = false; receivedshow = false; isguaranteed = false; cc = 0; // Offset for Extended Data if (strReplayShow.guaranteed == 0xFFFFFFFF) { isguaranteed = true; } if (strReplayShow.guideid != 0) { receivedshow = true; } if (strReplayShow.ProgramInfo.flags & 0x00000001) { iscc = true; } if (strReplayShow.ProgramInfo.flags & 0x00000002) { isstereo = true; } if (strReplayShow.ProgramInfo.flags & 0x00000004) { isrepeat = true; } if (strReplayShow.ProgramInfo.flags & 0x00000008) { issap = true; } if (strReplayShow.ProgramInfo.flags & 0x00000010) { isletterbox = true; } if (strReplayShow.ProgramInfo.flags & 0x00000020) { // Movie, load extra 8 bytes into MovieInfo structure. ismovie = true; MoveMemory(&strMovieInfo,szOriginalDescriptionBuffer + cc,sizeof(strMovieInfo)); ConvertMovieInfoEndian(&strMovieInfo); memset(szBuffer,0,sizeof(szBuffer)); cc = cc + sizeof(strMovieInfo); MoveMemory(szBuffer,szOriginalDescriptionBuffer + cc,sizeof(szOriginalDescriptionBuffer) - cc); // this won't do the half stars, it's too cheesy for that! i = strMovieInfo.stars / 10; szStarRating[i] = 0; // MPAA Rating DisplayMPAARating(strMovieInfo.mpaa,szMPAARating); DisplayExtendedMPAARating(strMovieInfo.mpaa,szExtMPAA,false); if (strlen(szMPAARating) > 0) { if (strlen(szExtMPAA) > 0) { strcat( szMPAARating, " (" ); strcat( szMPAARating, szExtMPAA ); strcat( szMPAARating, ")" ); } } } if (strReplayShow.ProgramInfo.flags & 0x00000040) { // Multiple Parts hasparts = true; MoveMemory(&strPartsInfo,szOriginalDescriptionBuffer + cc,sizeof(strPartsInfo)); ConvertPartsInfoEndian(&strPartsInfo); memset(szBuffer,0,sizeof(szBuffer)); cc = cc + sizeof(strPartsInfo); MoveMemory(szBuffer,szOriginalDescriptionBuffer + cc,sizeof(szOriginalDescriptionBuffer) - cc); } if (strReplayShow.ProgramInfo.flags & 0x00000080) { isppv = true; } if (!ismovie) { /* ** ** NOTE: ** This is what the Replay does. However, the TMS data does support a mix ** so, if you want, you can remove "if (!ismovie)". */ DisplayTVRating(strReplayShow.ProgramInfo.flags,szTVRating); DisplayExtendedTVRating(strReplayShow.ProgramInfo.flags,szExtTV,false); } if (strlen(szTVRating) > 0) { if (strlen(szExtTV) > 0) { strcat( szTVRating, " (" ); strcat( szTVRating, szExtTV ); strcat( szTVRating, ")" ); } } i = cc; // set offset (needed if it's been mangled by HasParts or IsMovie) strncpy(szShowTitle,strReplayShow.ProgramInfo.szDescription + i,strReplayShow.ProgramInfo.titleLen); i = i + strReplayShow.ProgramInfo.titleLen; strncpy(szEpisodeTitle,strReplayShow.ProgramInfo.szDescription + i,strReplayShow.ProgramInfo.episodeLen); i = i + strReplayShow.ProgramInfo.episodeLen; // Convert Codepages ConvertCodepage(strReplayShow.ProgramInfo.szDescription); ConvertCodepage(szShowTitle); ConvertCodepage(szEpisodeTitle); // Build Strings DisplayQuality(strReplayShow.quality,szQuality); // Display Record strcat(szOutputBuffer, "\n"); strcat(szOutputBuffer,"\t"); if (szEpisodeTitle[0] > 31) { sprintf(szBuffer,"%s \"%s\"",szShowTitle,szEpisodeTitle); strcat(szOutputBuffer, szBuffer); } else { if (ismovie) { if (szStarRating[0] != 0) { if (strMovieInfo.stars % 10) { sprintf(szBuffer,"%s (%s1/2, %s, %d",szShowTitle,szStarRating,szMPAARating,strMovieInfo.year); strcat(szOutputBuffer, szBuffer); } else { sprintf(szBuffer,"%s (%s, %s, %d",szShowTitle,szStarRating,szMPAARating,strMovieInfo.year); strcat(szOutputBuffer, szBuffer); } } else { sprintf(szBuffer,"%s (%s, %d",szShowTitle,szMPAARating,strMovieInfo.year); strcat(szOutputBuffer, szBuffer); } } else { strcat(szOutputBuffer, szShowTitle); } } // display flags cc = 0; i = 0; if (iscc) { cc++; } if (isstereo) { cc++; } if (isrepeat) { cc++; } if (issap) { cc++; } if (isppv) { cc++; } if (szTVRating[0] > 31) { cc++; } if (isletterbox) { cc++; } if (cc > 0) { if (ismovie) { strcat(szOutputBuffer,", "); } else { strcat(szOutputBuffer," ("); } i = 1; } else { if (ismovie) { strcat(szOutputBuffer,")"); } } if (szTVRating[0] > 31) { strcat(szOutputBuffer,szTVRating); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (iscc) { strcat(szOutputBuffer,"CC"); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (isstereo) { strcat(szOutputBuffer,"Stereo"); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (isrepeat) { strcat(szOutputBuffer,"Repeat"); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (issap) { strcat(szOutputBuffer,"SAP"); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (isppv) { strcat(szOutputBuffer,"PPV"); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (isletterbox) { strcat(szOutputBuffer,"Letterboxed"); cc--; if (cc) { strcat(szOutputBuffer,", "); } } if (i > 0) { strcat(szOutputBuffer,")"); } if (hasparts) { sprintf(szBuffer,"Part %d of %d",strPartsInfo.partnumber,strPartsInfo.maxparts); strcat(szOutputBuffer, szBuffer); } strcat(szOutputBuffer,"\n"); sprintf(szBuffer,"\t%s\n",szQuality); strcat(szOutputBuffer, szBuffer); sprintf(szBuffer,"\t%s\n",UnixTimeToString(strReplayShow.recorded)); strcat(szOutputBuffer, szBuffer); sprintf(szBuffer,"\tVideo/%ld.mpg\n",strReplayShow.recorded); strcat(szOutputBuffer, szBuffer); strcat(szOutputBuffer,"\t"); i = strReplayShow.ProgramInfo.minutes + strReplayShow.afterpadding + strReplayShow.beforepadding; cc = CalculateMinutes(strReplayShow.seconds); if (cc != i) { // This can happen if the cable goes out during recording or if you hit STOP etc. sprintf(szBuffer,"%d minutes ( %d scheduled )",cc,i); } else { sprintf(szBuffer,"%d minutes",cc); } strcat(szOutputBuffer, szBuffer); strcat(szOutputBuffer,"\n"); sprintf(szBuffer,"\t%d\n",cc); strcat(szOutputBuffer, szBuffer); strcat(szOutputBuffer,"\n"); fptr += readsize; } } while ( moreshows == true ); strcat(szOutputBuffer, "\n"); // Done, shut it down //printf("Done! %u ReplayShows parsed!\n", curshow); return 0; } }