diff --git a/zefie_wtvp_minisrv/includes/ServiceDeps/wtv-1800/tellyscripts/bf0app/bf0app.base.template.txt b/zefie_wtvp_minisrv/includes/ServiceDeps/wtv-1800/tellyscripts/base.template.txt similarity index 87% rename from zefie_wtvp_minisrv/includes/ServiceDeps/wtv-1800/tellyscripts/bf0app/bf0app.base.template.txt rename to zefie_wtvp_minisrv/includes/ServiceDeps/wtv-1800/tellyscripts/base.template.txt index 6f47482a..90153a42 100644 --- a/zefie_wtvp_minisrv/includes/ServiceDeps/wtv-1800/tellyscripts/bf0app/bf0app.base.template.txt +++ b/zefie_wtvp_minisrv/includes/ServiceDeps/wtv-1800/tellyscripts/base.template.txt @@ -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. diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/wtv-1800/preregister.js b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-1800/preregister.js index 3b2dce53..a4166f3b 100644 --- a/zefie_wtvp_minisrv/includes/ServiceVault/wtv-1800/preregister.js +++ b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-1800/preregister.js @@ -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 { diff --git a/zefie_wtvp_minisrv/includes/classes/WTVTellyScript.js b/zefie_wtvp_minisrv/includes/classes/WTVTellyScript.js index 2c160d91..6352ed67 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVTellyScript.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVTellyScript.js @@ -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, diff --git a/zefie_wtvp_minisrv/test_prereg.js b/zefie_wtvp_minisrv/test_prereg.js index 763f5741..6243d2dc 100644 --- a/zefie_wtvp_minisrv/test_prereg.js +++ b/zefie_wtvp_minisrv/test_prereg.js @@ -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(); });