implement preprocessor for tsf format

This commit is contained in:
zefie
2025-02-23 12:02:30 -05:00
parent 02187ec813
commit d3d4728ac5
4 changed files with 296 additions and 11 deletions

View File

@@ -1,5 +1,6 @@
/* TLLY ver=77 */
/* *=* Copyright 1996, 1997 WebTV Networks, Inc. All rights reserved. */
#ifndef FIJI
/*
* This is the "base" TellyScript Fragment. It is suppressed for FIJI
* (see comments below).
@@ -164,11 +165,12 @@ char* gPAPPassword; /* box's PAP password, embedded in script by the
int gEnable56K; /* if set, allow 56K connections; if not, don't */
int gDisguiseRate; /* if set, reverse the DTE and DCE rates */
int gFlexKnob=5; /* 56k knob for Rockwell, 5=most aggressive (default) */
#endif /*!FIJI*/
/* FIJI doesn't have these built-in */
int gBlock911=1; /* This flag is now set for all services. */
int gWantsVideoAd; /* if set, user wants to download Video Ads */
/* This flag is not needed anymore, left here for future use */
#ifndef FIJI
/*
* ===========================================================================
@@ -431,6 +433,97 @@ GetPhoneSettings(int* pSettingsVersion)
* ===========================================================================
*/
#ifndef CLASSIC
/*
* GetHardwareInfo - figure out some things about the platform we're
* running on.
*
* "*pIsEarlyK56" is set if we're using Rockwell's v1.0 K56 firmware.
* "*pAcceptsS220" is set if the modem recognizes S220 as a valid S-register.
*
* Assumes that the modem is happy and in V1E0 (verbose result code, no
* echo) mode.
*
* NOTE: we don't really check for and try to handle the case where the
* modem goes out to lunch and we're hitting the getline() timeouts. Since
* we've already talked to the modem a little before getting here, I think
* it's safe to assume that we won't fail here and then succeed later. If
* the information we get from this function is more critical than what we
* have now, we should be more careful.
*
* Returns 0 if all went well, nonzero on error.
*/
GetHardwareInfo(int* pIsEarlyK56, int* pAcceptsS220, int* pIsRockwell,
int* pAcceptsAmpP, int* pAcceptsPlusMR)
{
char resultBuf[64];
/* look for v1.0 Rockwell K56 firmware... if found, limit to <= V.34 */
flush();
sendstr("ATI3\r"); /* modem firmware query */
printf("TS: SENT ATI3");
getline(resultBuf, 63, 300); /* throw out the first one */
if (!getline(resultBuf, 63, 300)) /* keep 2nd; note it has leading '\r' */
return 1;
printf("TS: fw query got '%s'", resultBuf+1);
setfirmwareversion(resultBuf+1);
resultBuf[21] = '\0'; /* me falta strncmp in 1.0 */
*pIsEarlyK56 = 0;
if (strcmp(resultBuf+1, "V1.001_WEBTV-K56_DLP") == 0)
*pIsEarlyK56 = 1;
/*
* Softmodem can't handle AT+MS, so try to detect Rockwell. Sadly,
* there doesn't seem to be a simple way to do this.
*/
*pIsRockwell = 0;
if (strcmp(resultBuf+11, "TV-K56_DLP") == 0)
*pIsRockwell = 1;
resultBuf[15] = '\0'; /* do we have strncmp in 2.0? */
if (strcmp(resultBuf+8, "K56_DLP") == 0) /* V2.200-K56_DLP_RAM */
*pIsRockwell = 1;
printf("TS: isRockwell=%d", *pIsRockwell);
/* see if it's a Rockwell that supports +MR */
*pAcceptsPlusMR = 0;
if (*pIsRockwell) {
flush();
sendstr("AT+MR=0\r");
getline(resultBuf, 63, 300); /* throw out 1st */
if (!getline(resultBuf, 63, 300)) /* keep 2nd */
return 1;
printf("TS: +MR=0 test returned '%s'", resultBuf+1);
if (strcmp(resultBuf+1, "OK") == 0)
*pAcceptsPlusMR = 1;
}
/* see if it will take an S220 command */
flush();
sendstr("ATS220=0\r"); /* JP CW --> extremely insensitive */
printf("TS: SENT ATS220=0");
getline(resultBuf, 63, 300); /* throw out 1st */
if (!getline(resultBuf, 63, 300)) /* keep 2nd */
return 1;
printf("TS: S220 test returned '%s'", resultBuf+1);
*pAcceptsS220 = 0;
if (strcmp(resultBuf+1, "OK") == 0)
*pAcceptsS220 = 1;
/* see if it will take an &Pn command -- desktop Supras won't */
flush();
sendstr("AT&P0\r");
printf("TS: SENT AT&P0");
getline(resultBuf, 63, 300); /* throw out 1st */
if (!getline(resultBuf, 63, 300)) /* keep 2nd */
return 1;
printf("TS: AT&P0 test returned '%s'", resultBuf+1);
*pAcceptsAmpP = 0;
if (strcmp(resultBuf+1, "OK") == 0)
*pAcceptsAmpP = 1;
return 0;
}
#endif
/*
* Initialize - set up the modem configuration and dialing commands, and
@@ -454,6 +547,11 @@ Initialize(char* staticConfigBuf, char* dynamicConfigBuf)
char dialSpeed = settings[104];
char cwSensitivity = extendedSettings[105]; /* version >= 7 */
char *cwValue = "14";
#ifndef CLASSIC
char *s220Value;
int isEarlyK56, acceptsS220, isRockwell, acceptsAmpP, acceptsPlusMR;
char *flexKnobStr; /* sairam testing */
#endif
/*
* Talk to the modem.
@@ -466,7 +564,40 @@ Initialize(char* staticConfigBuf, char* dynamicConfigBuf)
if (SendAndWaitForOK("AT&D2V1E0\r", "OK"))
return 3; /* kTellyConfigurationError */
#ifndef CLASSIC
/* figure out what we got? */
if (GetHardwareInfo(&isEarlyK56, &acceptsS220, &isRockwell, &acceptsAmpP,
&acceptsPlusMR))
return 3; /* kTellyConfigurationError */
if (isEarlyK56)
printf("TS: found v1.0 K56 fw, disabling 56K");
strcpy(staticConfigBuf, "AT");
if (isRockwell) {
if (isEarlyK56 || !gEnable56K) {
strcat(staticConfigBuf, "+MS=11,1"); /* disable 56K */
}
/*
else {
/+ Rockwell 56k Aggressiveness knob - sairam testing +/
if (isRockwell) {
sprintf(staticConfigBuf+2, "!0049=0%d", gFlexKnob);
printf("TS: setting 56k knob to %d", gFlexKnob);
}
}
*/
} else {
/* assume softmodem */
if (!gEnable56K) {
strcat(staticConfigBuf, "S51=31"); /* disable 56K */
}
}
if (acceptsPlusMR) /* suppress extended result codes we don't parse */
strcat(staticConfigBuf, "+MR=0");
#else
strcpy(staticConfigBuf, "AT"); /* CLASSIC doesn't support 56K */
#endif
/*
* Modem configuration. This sets things so we connect with V.34 only,
@@ -528,6 +659,26 @@ Initialize(char* staticConfigBuf, char* dynamicConfigBuf)
strcat(dynamicConfigBuf, "S10=");
strcat(dynamicConfigBuf, cwValue);
#ifndef CLASSIC
/* set the JP "catch-phone" sensitivity; this is *in addition* to S10 */
if (acceptsS220) {
if (cwSensitivity == 1) /* most likely to hang up */
s220Value = "32";
else if (cwSensitivity == 2)
s220Value = "21";
else if (cwSensitivity == 3)
s220Value = "11";
else /* == 4 or bogus */ /* most likely to ignore calls */
s220Value = "1";
printf("TS: setting S220=%s", s220Value);
strcat(dynamicConfigBuf, "S220=");
strcat(dynamicConfigBuf, s220Value);
} else {
printf("TS: not setting S220"); /* CLASSIC boxes; others? */
}
#endif
/*
* Set the dial speed.
*/
@@ -541,6 +692,22 @@ Initialize(char* staticConfigBuf, char* dynamicConfigBuf)
strcat(dynamicConfigBuf, "S11=1"); /* blazing (not in UI) */
}
#ifndef CLASSIC
/*
* Set the pulse speed. &P0 is default for US, but we use JP pulse
* values for now since they seem to work anyway. Some desktop Supra
* modems don't understand "&Pn", and will fail to configure, so we
* use the "acceptsAmpP" auto-detect value.
*/
if (acceptsAmpP) {
if (dialSpeed == 0 || dialSpeed == 1) {
strcat(dynamicConfigBuf, "&P1"); /* 10PPS 33% */
} else if (dialSpeed == 2 || dialSpeed == 3) {
strcat(dynamicConfigBuf, "&P3"); /* 20PPS 33% */
}
}
#endif
/* put a carriage return on the end */
strcat(dynamicConfigBuf, "\r");
@@ -699,6 +866,18 @@ ParseResult(int result)
int retcode;
char* comment = "";
#ifndef CLASSIC /* no CLASSIC box supports modem_parsersult() */
retcode = modem_parseresult(&result, &gDTERate, &gDCERate, &gCompression,
&gProtocol, &gConnected, &comment);
if (retcode != 0x42554646) {
printf("TS: parseresult -- %d %s (retcode=%d)", result,
comment, retcode);
if (gDCERate && gDCERate < 14400) {
retcode = 14; /* kTellyVerySlowConnect */
}
return retcode;
}
#endif
retcode = 0;
if (result == 0) /* OK */
@@ -1285,11 +1464,34 @@ DialByIndex(char* staticConfig, char* dynamicConfig, char* sequence)
return err;
} else {
dialerror(err);
#ifndef CLASSIC
/* 101223/093628
* Make sure that MsgWatch calls are
* limited to the primary POP only,
* and to the first trial!
*/
if (idx == nextNumber) {
if (getconnectreason() == 1) {
return err;
}
}
#endif
}
} else if (status == 2 || idx == sequenceLen-1) {
return err;
} else /*status==1*/ {
dialerror(err);
#ifndef CLASSIC
/* 101223/093628
* Make sure that MsgWatch calls are
* limited to the primary POP only
*/
if (idx == nextNumber) {
if (getconnectreason() == 1) {
return err;
}
}
#endif
}
}
@@ -1369,6 +1571,9 @@ main()
return result;
}
}
#endif /* !FIJI */
/* --- base.tsf END --- */
/*
* Locale-specific stuff for USA.

View File

@@ -1,5 +1,6 @@
var minisrv_service_file = true;
var template_path = "";
var template = "";
var template_preprocessor = {};
var gourl = "wtv-head-waiter:/login?";
@@ -109,7 +110,8 @@ if (session_data.data_store.wtvsec_login) {
case "bf0app":
prereg_contype = "text/tellyscript";
// if wtv-open-access: true then client expects OpenISP
template = wtvshared.getServiceDep("/wtv-1800/tellyscripts/bf0app/bf0app.base.template.txt")
template = wtvshared.getServiceDep("/wtv-1800/tellyscripts/base.template.txt")
template_preprocessor = { 'CLASSIC': true }
if (session_data.get("wtv-open-access")) template += wtvshared.getServiceDep("/wtv-1800/tellyscripts/bf0app/bf0app.openisp.template.txt");
else template += wtvshared.getServiceDep("/wtv-1800/tellyscripts/bf0app/bf0app.normal.template.txt");
//else file_path = wtvshared.getServiceDep("/wtv-1800/tellyscripts/bf0app/bf0app_WTV_18006138199.tok", true);
@@ -119,6 +121,7 @@ if (session_data.data_store.wtvsec_login) {
case "JP-Fiji":
prereg_contype = "text/tellyscript";
template_preprocessor = { 'FIJI': true }
// if wtv-open-access: true then client expects OpenISP
if (session_data.get("wtv-open-access")) var file_path = wtvshared.getServiceDep("/wtv-1800/tellyscripts/FIJI/dc_production_normal.tok", true);
else var file_path = wtvshared.getServiceDep("/wtv-1800/tellyscripts/FIJI/dc_production_normal.tok", true);
@@ -201,11 +204,11 @@ if (session_data.data_store.wtvsec_login) {
});
} else if (template) {
request_is_async = true;
telly = new WTVTellyScript(template, 2); // 2 = Untokenized
telly = new WTVTellyScript(template, 2, template_preprocessor, session_data.get("wtv-open-access") ? 3 : 1); // dataState 2 = Untokenized
telly.setTemplateVars(minisrv_config.config.service_name, minisrv_config.services[service_name].dialin_number, minisrv_config.services[service_name].dns1ip, minisrv_config.services[service_name].dns2ip);
telly.minify();
telly.tokenize();
telly.pack((session_data.get("wtv-open-access")) ? 3 : 1);
telly.pack();
sendToClient(socket, headers, telly.packed_data);
}
} else {

View File

@@ -1053,17 +1053,93 @@ class WTVTellyScript {
* @param {Uint8Array|string} data - The TellyScript data (either packed, tokenized, or raw).
* @param {number} dataState - One of TellyScriptState (default: PACKED).
* @param {number} tellyscriptType - One of TellyScriptType (default: ORIGINAL).
* @param {object} preprocessor_definitions - A dictionary of preprocessor definitions.
* @param {number} version_minor - The minor version number (default: 1).
*/
constructor(data, dataState = TellyScriptState.PACKED, tellyscriptType = TellyScriptType.ORIGINAL) {
constructor(data, dataState = TellyScriptState.PACKED, preprocessor_definitions = {}, version_minor = 1, tellyscriptType = TellyScriptType.ORIGINAL) {
this.tellyscript_type = tellyscriptType;
this.packed_data = null;
this.packed_header = null;
this.tokenized_data = null;
this.raw_data = null;
this.preprocessor_definitions = preprocessor_definitions;
this.version_minor = version_minor;
this.process(data, dataState);
}
preprocess() {
var definitions = this.preprocessor_definitions || {};
// Split input into lines (handling CRLF and LF)
const lines = this.raw_data.split(/\r?\n/);
const output = [];
// A stack to track whether the current block is active.
// Start with "true" so that top-level lines are output.
const stateStack = [true];
// Process each line one by one.
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Only process directives if they are left-aligned.
if (line.startsWith("#")) {
if (/^#ifdef\b/.test(line)) {
// Get the label immediately after "#ifdef"
const token = line.slice(6).split(/\s/)[0];
const condition = !!definitions[token];
// The block is active only if the parent block is active and condition is true.
const active = stateStack[stateStack.length - 1] && condition;
stateStack.push(active);
continue; // Do not output this directive line.
} else if (/^#ifndef\b/.test(line)) {
const token = line.slice(7).split(/\s/)[0];
const condition = !definitions[token];
const active = stateStack[stateStack.length - 1] && condition;
stateStack.push(active);
continue;
} else if (/^#if\b/.test(line)) {
// Expect exactly "#if 1" or "#if 0" (no extra spaces allowed).
const token = line.slice(3).split(/\s/)[0];
if (token !== "1" && token !== "0") {
throw new Error(
`Invalid #if condition at line ${i + 1}: "${line}"`
);
}
const condition = token === "1";
const active = stateStack[stateStack.length - 1] && condition;
stateStack.push(active);
continue;
} else if (/^#else\b/.test(line)) {
if (stateStack.length <= 1) {
throw new Error(`#else without matching #if at line ${i + 1}`);
}
// Flip the state of the current block while considering the parent's state.
const previous = stateStack.pop();
const newState = stateStack[stateStack.length - 1] && !previous;
stateStack.push(newState);
continue;
} else if (/^#endif\b/.test(line)) {
if (stateStack.length <= 1) {
throw new Error(`#endif without matching #if at line ${i + 1}`);
}
stateStack.pop();
continue;
} else if (/^#include\b/.test(line)) {
// Silently remove #include directives.
continue;
}
}
// For non-directive lines (or lines with unrecognized directives),
// output them only if the current block is active.
if (stateStack[stateStack.length - 1]) {
output.push(line);
}
}
this.raw_data = output.join("\n");
}
minify() {
let minifier = new WTVTellyScriptMinifier();
this.raw_data = minifier.minify(this);
@@ -1218,12 +1294,13 @@ class WTVTellyScript {
this.pack();
this.detokenize();
} else if (dataState === TellyScriptState.RAW) {
// For RAW byte data, convert to string (assuming UTF-8)
// For RAW byte data, convert to string (assuming UTF-8)
this.process(new TextDecoder().decode(data), dataState);
}
} else if (typeof data === "string") {
if (dataState === TellyScriptState.RAW) {
this.raw_data = data;
this.preprocess()
this.tokenize();
this.pack();
} else if (dataState === TellyScriptState.PACKED || dataState === TellyScriptState.TOKENIZED) {
@@ -1275,7 +1352,7 @@ class WTVTellyScript {
}
// --- Packing ---
pack(version_minor = 1) {
pack() {
// Compress tokenized data using LZSS.
const comp = new LZSS();
const compressed_data = comp.compress(this.tokenized_data);
@@ -1295,7 +1372,7 @@ class WTVTellyScript {
this.packed_header = {
magic: (this.tellyscript_type === TellyScriptType.DIALSCRIPT) ? "VKAT" : "ANDY",
version_major: (this.packed_header && this.packed_header.version_major) ? this.packed_header.version_major : 1,
version_minor: (this.packed_header && this.packed_header.version_minor) ? this.packed_header.version_minor : version_minor,
version_minor: (this.packed_header && this.packed_header.version_minor) ? this.packed_header.version_minor : this.version_minor,
script_id: script_id,
script_mod: Math.floor(Date.now() / 1000),
compressed_data_length: compressed_data.length,

View File

@@ -22,7 +22,6 @@ wtv-system-version: 7181
wtv-capability-flags: 10935ffc8f
wtv-client-bootrom-version: 2046
wtv-client-rom-type: bf0app
wtv-open-access: true
wtv-system-chipversion: 51511296
User-Agent: Mozilla/4.0 WebTV/2.2.6.1 (compatible; MSIE 4.0)
wtv-encryption: true
@@ -30,6 +29,7 @@ wtv-script-id: 0
wtv-script-mod: 0
`
//wtv-open-access: true
//wtv-client-rom-type: US-LC2-disk-0MB-8MB
@@ -62,7 +62,7 @@ client.on('data', (chunk) => {
telly = new WTVTellyScript(accumulatedBuffer)
console.log(telly.packed_header)
console.log(telly.raw_data)
// console.log(telly.raw_data)
// Optionally, close the connection after receiving data
client.end();
});