Files
Rocky5 921f9fe9f8 Clean Copy
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
2024-01-08 19:01:29 +00:00

1580 lines
51 KiB
C++

#pragma code_seg( "RTV_TEXT" )
#pragma data_seg( "RTV_DATA" )
#pragma bss_seg( "RTV_BSS" )
#pragma const_seg( "RTV_RD" )
/*
** ReplayTV 5000
** Guide Parser
** by Lee Thompson <thompsonl@logh.net> Dan Frumin <rtv@frumin.com>, Todd Larason <jtl@molehill.org>
**
** 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 <andrew@biliskner.demon.co.uk>
***************************************************************************************************************
*/
#ifdef _XBOX
#include <Xtl.h>
#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 <winsock2.h>
#endif
#include <winnt.h>
#include <winbase.h>
#endif
#if defined(__unix__) && !defined(__FreeBSD__)
#include <netinet/in.h>
#endif
#include <string.h>
#include <memory.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#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 __unix__
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;
extern long _timezone;
extern char *_tzname[2];
#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");
tzbias = _timezone;
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
//-------------------------------------------------------------------------
#ifdef __unix__
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)
{
bool exitloop = false;
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
#ifdef __unix__
// UNIX specific translations
#endif
}
return;
}
//-------------------------------------------------------------------------
// Misc. Functions
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
int CalculateMinutes( int seconds )
{
int original = 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
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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));
int cc = sizeof(llValue) - 1;
int i = 0;
for( i = 0; i < sizeof(llValue); ++i )
{
szInt64[i] = szBuffer[cc];
--cc;
}
MoveMemory(&retval,szInt64,sizeof(retval));
return retval;
}
//-------------------------------------------------------------------------
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,"");
// 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, "<REPLAYGUIDE>\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, "<ITEM>\n");
strcat(szOutputBuffer,"\t<DISPLAYNAME>");
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,"</DISPLAYNAME>\n");
sprintf(szBuffer,"\t<QUALITY>%s</QUALITY>\n",szQuality);
strcat(szOutputBuffer, szBuffer);
sprintf(szBuffer,"\t<RECORDED>%s</RECORDED>\n",UnixTimeToString(strReplayShow.recorded));
strcat(szOutputBuffer, szBuffer);
sprintf(szBuffer,"\t<PATH>Video/%u.mpg</PATH>\n",strReplayShow.recorded);
strcat(szOutputBuffer, szBuffer);
strcat(szOutputBuffer,"\t<DURATION>");
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,"</DURATION>\n");
sprintf(szBuffer,"\t<SIZE>%d</SIZE>\n",cc);
strcat(szOutputBuffer, szBuffer);
strcat(szOutputBuffer,"</ITEM>\n");
fptr += readsize;
}
}
while ( moreshows == true );
strcat(szOutputBuffer, "</REPLAYGUIDE>\n");
// Done, shut it down
//printf("Done! %u ReplayShows parsed!\n", curshow);
return 0;
}
}