Try   HackMD

Majora's Mask Gateway changes

Introduction

The build date of the newly-discovered Gateway rom is 00-08-08 09:25:41. This is between the US retail version (00-07-31, referred to as NE0) and the first PAL version (00-09-25, referred to as NP0)

Summary

Most of the rom is the same as NE0. A number of patches/changes previously only known in NP0 and after are also present, surprising considering that this rom was built only 10 days after NE0.

  • Several unused functions are removed from various files, as they are in NP0.
  • Several patches from NP0 (in z_eventmgr, z_kaleido_setup), but not all.
  • The file choice screen is changed to only display a single file, and no copy/erase functionality. A new file (which we may guess is called gateway_title_static) contains 3 new textures for this, a Start button, and textures for Please Select., Press A button to decide., Press B button to exit.. The Options screen is changed to remove the Dolby logo. After entering a name, the game starts immediately END is pressed.
  • Other standard gateway-compliant changes (removing rumble capability, adding and enabling the pause/unpause code).
  • Some as yet not understood changes to the save system to avoid actually saving data.
  • Owl statues act as in the Japanese version: you cannot save using them.
  • The most striking difference is in the message_data_static file: somehow all the apostrophes are missing! There are also changes to the "Save and return to Dawn of the First Day" box, since it no longer saves.

Detailed analysis

Section titles are the segment name.

Rom header

Region byte is 'G'.

code

N.B. the segment is called code, but there is executable code in many other segments, this is just the largest one that is always loaded.

  • z_collision_check: Collider_SetTrisDim removed (as in NP0)
  • z_demo: CutsceneCmd_Misc: Sram_ResetSaveFromMoonCrash in case CS_MISC_RESET_SAVE_FROM_MOON_CRASH replaced by func_80144A94/func_80144924_neg
  • z_eventmgr: CutsceneManager_Start:
    ​​​​s32 csType = 0;
    
    ​​​​+sCutsceneMgr.play->actorCtx.flags &= ~ACTORCTX_FLAG_PICTO_BOX_ON;
    
    ​​​​if ((csId < 0) || (sCutsceneMgr.csId != CS_ID_NONE)) {
    ​​​​    return csId;
    ​​​​}
    
    (as in NP0)
  • z_kaleido_setup: KaleidoSetup_Update
    ​​if (!(play->actorCtx.flags & ACTORCTX_FLAG_1) && !(play->actorCtx.flags & ACTORCTX_FLAG_PICTO_BOX_ON)) {
    ​​+    if (player->actor.id == ACTOR_PLAYER) {
    ​​        if ((play->actorCtx.unk268 == 0) && CHECK_BTN_ALL(input->press.button, BTN_START)) {
    ​​[...]
    ​​+    }
    
    (as in NP0)
  • z_rcp: last function removed (as in NP0)
  • z_rumble:
    ​​​​void Rumble_Update(void* arg0) {
    ​​​​    RumbleManager_Update(&gRumbleMgr);
    ​​-    PadMgr_RumbleSet(gRumbleMgr.rumbleEnabled);
    ​​​​}
    ​​​​[...]
    ​​​​s32 Rumble_ControllerOneHasRumblePak(void) {
    ​​-    return PadMgr_ControllerHasRumblePak(0);
    ​​+    return false;
    ​​​​}
    
  • z_sram:
    • Sram_ResetSaveFromMoonCrash: everything above gSaveContext.save.cutsceneIndex = cutsceneIndex; replaced by
      ​​​​​​​​Sram_SaveEndOfCycle(&gSaveContext);
      ​​​​​​​​gSaveContext.timerStates[TIMER_ID_MOON_CRASH] = TIMER_STATE_OFF;
      
      Also appears to take gamestate instead of sramCtx.
    • func_80145698 removed
    • Sram_SaveSpecialEnterClockTown stubbed
    • Sram_SaveSpecialNewDay:
      ​​​​​​​​void Sram_SaveSpecialNewDay(PlayState* play) {
      ​​​​​​​​    s32 cutsceneIndex = gSaveContext.save.cutsceneIndex;
      ​​​​​​​​    s32 day;
      ​​​​​​​​    u16 time = gSaveContext.save.time;
      
      ​​​​​​​​    day = gSaveContext.save.day;
      
      ​​​​​​​​    CLEAR_WEEKEVENTREG(WEEKEVENTREG_84_20);
      
      ​​​​​​​​    Sram_SaveEndOfCycle(play);
      ​​​​​​-      func_8014546C(&play->sramCtx);
      
      ​​​​​​​​    gSaveContext.save.day = day;
      ​​​​​​​​    gSaveContext.save.time = time;
      ​​​​​​​​    gSaveContext.save.cutsceneIndex = cutsceneIndex;
      ​​​​​​-      func_80185F64(play->sramCtx.saveBuf, D_801C67C8[gSaveContext.fileNum * 2], D_801C67F0[gSaveContext.fileNum * 2]);
      ​​​​​​​​}
      
  • New file at offset 0xA15F0: copy of OoT's z_ss_sram with an extra read and write function at the bottom. https://decomp.me/scratch/glaGm
  • z_message: Message_Update
    ​​      case MSGMODE_NEW_CYCLE_0:
    ​​          play->state.unk_A3 = 1;
    ​​          sp44 = gSaveContext.save.cutscene;
    ​​          sp3E = gSaveContext.save.time;
    ​​          sp40 = gSaveContext.save.day;
    
    ​​          Sram_SaveEndOfCycle(play);
    ​​          gSaveContext.timerStates[3] = 0;
    ​​-          func_8014546C(sramCtx);
    
    ​​          gSaveContext.save.day = sp40;
    ​​          gSaveContext.save.time = sp3E;
    ​​          gSaveContext.save.cutscene = sp44;
    
    ​​-          if (gSaveContext.fileNum != 0xFF) {
    ​​-              func_80147008(sramCtx, D_801C67C8[gSaveContext.fileNum * 2], D_801C6818[gSaveContext.fileNum * 2]);
    ​​-              func_80147020(sramCtx);
    ​​-          }
    ​​-          msgCtx->msgMode = MSGMODE_NEW_CYCLE_1;
    ​​+          play->msgCtx.ocarinaMode = OCARINA_MODE_APPLY_SOT;
    ​​+          msgCtx->msgMode = MSGMODE_NEW_CYCLE_2;
    ​​          break;
    
  • graph: function to conform to the gateway halting requirements, i.e. pause gameplay when joypad Up+Down is pressed and released, resume when joypad Left+Right is pressed and released. This is called near the top of graph_main(Graph_UpdateGame)
    ​​​​void func_800A1EF8_neg(GameState* game) {
    ​​​​    Input* input = game->input;
    ​​​​    u16 payload;
    
    ​​​​    if (CHECK_BTN_ALL(input->cur.button, U_JPAD | D_JPAD)) {
    ​​​​        SREG(20) = 2;
    
    ​​​​        // Set halt bit
    ​​​​        payload = 0xFFFF;
    ​​​​        func_80091BC4_neg(&payload);
    
    ​​​​        // Wait for Up or Down to be released
    ​​​​        do {
    ​​​​            usleep(1000000 / 60);
    ​​​​            game_get_controller(game);
    ​​​​        } while (CHECK_BTN_ALL(input->cur.button, U_JPAD | D_JPAD));
    
    ​​​​        // Sit in this loop until Left and Right detected
    ​​​​        do {
    ​​​​            usleep(1000000 / 60);
    ​​​​            game_get_controller(game);
    ​​​​        } while (!CHECK_BTN_ALL(input->cur.button, L_JPAD | R_JPAD));
    
    ​​​​        // clear halt bit
    ​​​​        payload = 0;
    ​​​​        func_80091BC4_neg(&payload);
    
    ​​​​        // Wait for Left or Right to be released
    ​​​​        do {
    ​​​​            usleep(1000000 / 60);
    ​​​​            game_get_controller(game);
    ​​​​        } while (CHECK_BTN_ALL(input->cur.button, L_JPAD | R_JPAD));
    
    ​​​​        SREG(20) = 0;
    ​​​​    }
    ​​​​}
    
    (very similar to the OoT version but uses different macros and the ss_sram wrappers). SREG(20) disables audio updating.
  • padmgr:
    • PadMgr_UpdateRumble, PadMgr_RumbleStop, PadMgr_RumbleReset, PadMgr_RumbleSetSingle, PadMgr_RumbleSet, PadMgr_ControllerHasRumblePak all removed (rumble-related)
    • Padmgr_ParseState:
      ​​​​​​​​-      // If opposed directions on the D-Pad are pressed at the same time, mask both out
      ​​​​​​​​-      if ((input->cur.button & (D_JPAD | U_JPAD)) == (D_JPAD | U_JPAD)) {
      ​​​​​​​​-          input->cur.button &= ~(D_JPAD | U_JPAD);
      ​​​​​​​​-      }
      ​​​​​​​​-      if ((input->cur.button & (R_JPAD | L_JPAD)) == (R_JPAD | L_JPAD)) {
      ​​​​​​​​-          input->cur.button &= ~(R_JPAD | L_JPAD);
      ​​​​​​​​-      }
      
      (removed to enable the Gateway pause combinations)
    • Padmgr_HandleRetrace:
      ​​​​​​​​    // Rumble Pak
      ​​​​​​​​-    if (gFaultMgr.msgId != 0) {
      ​​​​​​​​-        // If fault is active, no rumble
      ​​​​​​​​-        PadMgr_RumbleStop();
      ​​​​​​​​-    } else if (gPadMgrPtr->rumbleOffTimer > 0) {
      ​​​​​​​​-        // If the rumble off timer is active, no rumble
      ​​​​​​​​-        --gPadMgrPtr->rumbleOffTimer;
      ​​​​​​​​-        PadMgr_RumbleStop();
      ​​​​​​​​-    } else if (gPadMgrPtr->rumbleOnTimer == 0) {
      ​​​​​​​​-        // If the rumble on timer is inactive, no rumble
      ​​​​​​​​-        PadMgr_RumbleStop();
      ​​​​​​​​-    } else if (!gPadMgrPtr->isResetting) {
      ​​​​​​​​-        // If not resetting, update rumble
      ​​​​​​​​-        PadMgr_UpdateRumble();
      ​​​​​​​​-        --gPadMgrPtr->rumbleOnTimer;
      ​​​​​​​​-    }
      
    • Padmgr_HandlePreNMI:
      ​​​​​​​​void PadMgr_HandlePreNMI(void) {
      ​​​​​​​​    gPadMgrPtr->isResetting = true;
      ​​​​​​​​-    PadMgr_RumbleReset();
      ​​​​​​​​}
      
  • sys_math3d:
    • Unused functions func_8017A7B8, func_8017A7F8, func_8017A838 removed (individual cross product components) (as in NP0)
    • func_8017B68C removed (as in NP0)
  • sys_rumble: RumbleManager_Update: all function calls and
    ​​      if (D_801D1E70) {
    ​​          for (i = 0; i < MAXCONTROLLERS; i++) {
    ​​              PadMgr_RumbleSetSingle(i, false);
    ​​          }
    ​​      }
    
    removed.
  • c_keyframe: last function removed (as in NP0)

Overlays

  • ovl_file_choose:
    • FileSelect_DrawNameEntry: validName block that uses z_sram functions replaced with
      ​​​​this->configMode = CM_NAME_ENTRY_TO_MAIN;
      ​​​​this->actionTimer = 4;
      ​​​​this->selectedFileIndex = this->buttonIndex;
      ​​​​this->menuMode = FS_MENU_MODE_SELECT;
      ​​​​this->nextTitleLabel = FS_TITLE_OPEN_FILE;
      ​​​​this->selectMode = SM_FADE_OUT;
      ​​​​func_801A3558_neg(0xF); // audio function
      
    • FileChoose_UpdateOptionsMenu
      ​​​​    if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
      ​​​​        play_sound(NA_SE_SY_FSEL_DECIDE_L);
      ​​​​-        Sram_WriteSaveOptionsToBuffer(sramCtx);
      ​​​​-
      ​​​​-        if (!gSaveContext.flashSaveAvailable) {
      ​​​​            this->configMode = CM_OPTIONS_TO_MAIN;
      ​​​​-        } else {
      ​​​​-            Sram_SetFlashPagesDefault(sramCtx, gFlashSaveStartPages[8], gFlashSpecialSaveNumPages[8]);
      ​​​​-            Sram_StartWriteToFlashDefault(sramCtx);
      ​​​​-            this->configMode = CM_OPTIONS_WAIT_FOR_FLASH_SAVE;
      ​​​​-        }
      ​​​​-        func_801A3D98(gSaveContext.options.audioSetting);
      ​​​​        return;
      ​​​​    }
      
    • FileChoose_DrawOptionsImpl: changes mostly to remove the Dolby logo, it seems.
      • Vertices for the dividers are loaded in one set of 12 at the top instead of loading 4 at a time.
      • 20 instead of 32 are loaded before drawing the headers, the final iteration of the loop is skipped (not drawing the Dolby logo).
      • 24 instead of 32 are loaded before drawing the audio settings.
      • 8 more are loaded separately before drawing the "Check Brightness" bars, and used separately (probably the vtx variable was just reset to 0).
    • FileChoose_IncrementConfigMode:
      ​​​​void FileSelect_IncrementConfigMode(FileSelectState* this) {
      ​​​​-    this->configMode++;
      ​​​​}
      
    • FileChoose_InitModeUpdate: contents replaced by
      ​​​​if (gSaveContext.language > 2) {
      ​​​​    gSaveContext.language = 1; // English?
      ​​​​}
      ​​​​this->screenFillAlpha = 255;
      ​​​​this->menuMode = FS_MENU_MODE_CONFIG;
      ​​​​this->configMode = CM_FADE_IN_START;
      ​​​​this->titleLabel = FS_TITLE_SELECT_FILE;
      ​​​​this->nextTitleLabel = FS_TITLE_OPEN_FILE;
      
      while not much like any other version, the language set is similar to what PAL versions do. (It is not clear when the language would be set to something other than 1 on the US version anyway.)
    • FileChoose_UpdateMainMenu: majority of the function is removed, all that remains is
      ​​​​void FileChoose_UpdateMainMenu(GameState* thisx) {
      ​​​​    FileSelectState* this = (FileSelectState*)thisx;
      ​​​​    SramContext* sramCtx = &this->sramCtx;
      ​​​​    Input* input = CONTROLLER1(&this->state);
      
      ​​​​    if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
      ​​​​        if (this->buttonIndex == 0) {
      ​​​​            play_sound(NA_SE_SY_FSEL_DECIDE_L);
      ​​​​            this->configMode = CM_ROTATE_TO_NAME_ENTRY;
      ​​​​            this->kbdButton = FS_KBD_BTN_NONE;
      ​​​​            this->charPage = FS_CHAR_PAGE_HIRA;
      ​​​​            if (gSaveContext.options.language != 0) {
      ​​​​                this->charPage = FS_CHAR_PAGE_ENG;
      ​​​​            }
      ​​​​            this->kbdX = 0;
      ​​​​            this->kbdY = 0;
      ​​​​            this->charIndex = 0;
      ​​​​            this->charBgAlpha = 0;
      ​​​​            this->newFileNameCharCount = 0;
      ​​​​            this->nameEntryBoxPosX = 120;
      ​​​​            this->nameEntryBoxAlpha = 0;
      ​​​​            Lib_MemCpy(&this->fileNames[this->buttonIndex], &sEmptyName, ARRAY_COUNT(sEmptyName));
      ​​​​        } else {
      ​​​​            play_sound(NA_SE_SY_FSEL_DECIDE_L);
      ​​​​            this->prevConfigMode = this->configMode;
      ​​​​            this->configMode = CM_MAIN_TO_OPTIONS;
      ​​​​            this->kbdButton = FS_KBD_BTN_HIRA;
      ​​​​            this->kbdX = 0;
      ​​​​            this->kbdY = 0;
      ​​​​            this->charBgAlpha = 0;
      ​​​​            this->newFileNameCharCount = 0;
      ​​​​            this->nameEntryBoxPosX = 120;
      ​​​​            this->actionTimer = 4;
      ​​​​        }
      ​​​​    } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
      ​​​​        gSaveContext.gameMode = GAMEMODE_TITLE_SCREEN;
      ​​​​        STOP_GAMESTATE(&this->state);
      ​​​​        SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init, sizeof(TitleSetupState));
      ​​​​    } else {
      ​​​​        if (ABS(this->stickAdjY) > 30) {
      ​​​​            play_sound(NA_SE_SY_FSEL_CURSOR);
      ​​​​            arg0->buttonIndex ^= 1;
      ​​​​        }
      ​​​​    }
      ​​​​}
      
    • FileChoose_SetWindowContentVtx: changed, not clear how, function is too messy to work it out until both versions are matched. It contains some extra vtx setting, and menuMode section is essentially gone.
    • FileChoose_DrawWindowContents: very similar to OoT:
      • sTitleLabels[this->titleLabel] replaced by D_2000800_neg in the first gDPLoadTextureBlock
      • Next block (the nextTitleLabel one) is gone.
      • The file button/box loop is gone, replaced by a single iteration, that uses the Start texture.
      • sActionButtonTextures loop is gone.
      • The option buttons quadrangle uses different vertices.
    • FileChoose_LoadGame
      ​​​​-    gSaveContext.fileNum = this->buttonIndex;
      ​​​​-    Sram_OpenSave(this, &this->sramCtx);
      ​​​​+    Sram_InitNewSave();
      ​​​​+    for (i = 0; i < 8; i++) {
      ​​​​+        gSaveContext.save.playerData.playerName[i] = this->fileNames[this->buttonIndex][i];
      ​​​​+    }
      
    • FileChoose_Main: config TextureBlock/TextureRectangle replaced by
      ​​​​if ((this->configMode < CM_MAIN_TO_OPTIONS) || (this->configMode > CM_OPTIONS_TO_MAIN)) {
      ​​​​    gDPLoadTextureBlock(POLY_OPA_DISP++, D_2001000_neg, G_IM_FMT_IA, G_IM_SIZ_8b, 144, 16, 0,
      ​​​​                        G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 
      ​​​​                        G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
      ​​​​    gSPTextureRectangle(POLY_OPA_DISP++, 90 << 2, 204 << 2, (90 + 144) << 2, (204 + 16) << 2, 
      ​​​​                        G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
      ​​​​} else if (this->configMode == CM_OPTIONS_MENU) {
      ​​​​    gDPLoadTextureBlock(POLY_OPA_DISP++, D_2001900_neg, G_IM_FMT_IA, G_IM_SIZ_8b, 144, 16, 0,
      ​​​​                        G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 
      ​​​​                        G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
      ​​​​    gSPTextureRectangle(POLY_OPA_DISP++, 90 << 2, 204 << 2, (90 + 144) << 2, (204 + 16) << 2, 
      ​​​​                        G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
      ​​​​}
      
    • FileChoose_InitContext:
      ​​​​    Sram_Alloc(&this->state, &this->sramCtx);
      ​​​​-    func_801457CC(&this->state, &this->sramCtx);
      ​​​​+    Sram_InitNewSave();
      
    • FileChoose_Init:
      ​​​​-    size = SEGMENT_ROM_SIZE(parameter_static);
      ​​​​+    size = SEGMENT_ROM_SIZE(gateway_title_static);
      ​​​​    this->parameterSegment = THA_AllocTailAlign16(&this->state.heap, size);
      ​​​​-    DmaMgr_SendRequest0(this->parameterSegment, SEGMENT_ROM_START(parameter_static), size);
      ​​​​+    DmaMgr_SendRequest0(this->parameterSegment, SEGMENT_ROM_START(gateway_title_static), size);
      
      ​​​​-    size = gObjectTable[OBJECT_MAG].vromEnd - gObjectTable[OBJECT_MAG].vromStart;
      ​​​​-    this->titleSegment = THA_AllocTailAlign16(&this->state.heap, size);
      ​​​​-    DmaMgr_SendRequest0(this->titleSegment, gObjectTable[OBJECT_MAG].vromStart, size);
      
  • ovl_kaleido_scope:
    • func_80821A04_ne0/func_80820604_neg: gSaveContext.buttonStatus[EQUIP_SLOT_B] set to BTN_DISABLED instead of BTN_ENABLED.
    • func_80828788_ne0/func_8082738C_neg:
    ​​      pauseCtx->pageIndex = sPageSwitchNextPageIndex[pauseCtx->nextPageMode];
    ​​      pauseCtx->mainState = PAUSE_MAIN_STATE_IDLE;
    ​​      pauseCtx->state++; // PAUSE_STATE_MAIN
    ​​      pauseCtx->alpha = 255;
    ​​+      gSaveContext.buttonStatus[EQUIP_SLOT_B] = BTN_DISABLED;
    ​​      func_80115844(play, DO_ACTION_RETURN);
    
    • KaleidoScope_Update:
    ​​          if (pauseCtx->roll < -628.0f) {
    ​​              pauseCtx->roll = -628.0f;
    ​​              interfaceCtx->startAlpha = 255;
    ​​              sGameOverRectPosY = 66;
    ​​              sPauseMenuVerticalOffset = 0.0f;
    ​​              pauseCtx->alpha = 255;
    ​​-              if (gameOverCtx->state == GAMEOVER_INACTIVE) {
    ​​-                  pauseCtx->state = PAUSE_STATE_GAMEOVER_SAVE_PROMPT;
    ​​-              } else {
    ​​                  pauseCtx->state = PAUSE_STATE_GAMEOVER_CONTINUE_PROMPT;
    ​​-              }
    ​​+              gameOverCtx->state++;
    ​​          }
    
  • ovl_En_Bombf: unused vector removed
    ​​void EnBombf_Update(Actor* thisx, PlayState* play) {
    ​​    Vec3f sp8C = { 0.0f, 0.0f, 0.0f };
    ​​-    Vec3f sp80 = { 0.0f, 0.1f, 0.0f };
    ​​    Vec3f sp74 = { 0.0f, 0.0f, 0.0f };
    ​​    Vec3f sp68;
    
    (as in NP0)
  • ovl_En_Wood02:
    ​​Gfx* D_808C4D70[] = {
    ​​      object_wood02_DL_007968,
    ​​      object_wood02_DL_007D38,
    ​​      object_wood02_DL_0080D0,
    ​​      NULL,
    ​​      NULL,
    ​​      NULL,
    ​​-      object_wood02_DL_007AD0,
    ​​-      object_wood02_DL_007E20,
    ​​-      object_wood02_DL_009340,
    ​​-      object_wood02_DL_000160,
    ​​-      object_wood02_DL_000440,
    ​​-      object_wood02_DL_000700,
    ​​  };
    
    These are displaylists for types that are unused in MM (bushes, for example). Nothing in the object is changed, and nothing in the code, so this will cause some nice out-of-bounds reads if unused types are spawned. (as in NP0)
  • ovl_En_Fishing: bss is 0x10 bytes smaller (reason unknown, although notably it is the same size as NJ0, the original Japanese release).
  • ovl_En_Dno: Unused function func_80A7153C stubbed. (as in NP0)
  • ovl_Obj_Warpstone: saving functionality disabled. This is done simply by swapping the text used when talking to it while "open" back to that used to the Japanese version, where you could not save at an owl statue.

Asset files

As in Ocarina of Time, a new file is added, gateway_title_static, with textures for a Start button, Please Select., Press A button to decide., Press B button to exit. (the file is identical to OoT's)

The only other changed asset file is message_data_static:

  • 83 empty messages are removed (these consist only of the message header and the END character \xBF, there are 3 in NEG and 86 in NE0)
  • The message displayed on playing Song of Time differs:
    NE0:
    ​​​​[CL:RED]Save[CL:DFLT] and return to the [CL:RED]Dawn of[\n]
    ​​​​the First Day[CL:DFLT]?[\n]
    ​​​​[CL:GRN][CHOICE:2]Yes[\n]
    ​​​​No[END_SQUARE]
    
    NEG:
    ​​​​[CL:SIL]Ret[CL:DFLT]urn to the [CL:RED]Dawn of[\n]
    ​​​​the First Day[CL:DFLT]?[\n]
    ​​​​[CL:GRN][CHOICE:2]Yes[\n]
    ​​​​No[END_SQUARE]
    
    (the formatting appears to be a mistake)
  • A far more egregious mistake is that every ' and " has been removed, e.g.
    NE0
    ​​​​You always did say, "I cannot die[\n]
    ​​​​until I've eaten 1000 tons of rock[\n]
    ​​​​sirloin!"[ALT_END_SQUARE][END_SQUARE]
    
    NEG
    ​​​​You always did say, I cannot die[\n]
    ​​​​until Ive eaten 1000 tons of rock[\n]
    ​​​​sirloin![ALT_END_SQUARE][END_SQUARE]