[Scummvm-git-logs] scummvm master -> 80ee54d155446d405c067bebf81cb9f3cdb2e4ba
neuromancer
noreply at scummvm.org
Thu Jul 30 10:40:58 UTC 2026
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
804a49598a COLONY: added missing details in the mac controls
80ee54d155 SCUMM: RA2: marked as rebel2-psx as unstable
Commit: 804a49598a0e66b3b400eb868067555a0da42c92
https://github.com/scummvm/scummvm/commit/804a49598a0e66b3b400eb868067555a0da42c92
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-30T12:40:44+02:00
Commit Message:
COLONY: added missing details in the mac controls
Changed paths:
engines/colony/colony.cpp
engines/colony/colony.h
engines/colony/intro.cpp
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c60f436a047..6ec45530618 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -470,21 +470,37 @@ void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *
engine->handleMenuAction(action);
}
-void ColonyEngine::syncMacMenuChecks() {
- if (!_macMenu)
- return;
+bool ColonyEngine::showSaveDialog() {
+ _system->lockMouse(false);
+ CursorMan.setDefaultArrowCursor();
+ CursorMan.showMouse(true);
+ const bool saved = saveGameDialog();
+ updateMouseCapture(true);
+ return saved;
+}
- Graphics::MacMenuItem *optionsMenu = _macMenu->getMenuItem(3);
- if (!optionsMenu)
- return;
+Graphics::MacMenuItem *ColonyEngine::macMenuItemForAction(int menuIndex, int action) {
+ Graphics::MacMenuItem *menu = _macMenu ? _macMenu->getMenuItem(menuIndex) : nullptr;
+ if (!menu)
+ return nullptr;
- if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionSound))
+ for (int i = 0; i < _macMenu->numberOfMenuItems(menu); i++) {
+ Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(menu, i);
+ if (item && _macMenu->getAction(item) == action)
+ return item;
+ }
+ return nullptr;
+}
+
+void ColonyEngine::syncMacMenuChecks() {
+ // inits.c:50-53. "Faster" is checked when polygon fill is OFF.
+ if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuOptions, kMenuActionSound))
_macMenu->setCheckMark(item, _soundOn);
- if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionCrosshair))
+ if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuOptions, kMenuActionCrosshair))
_macMenu->setCheckMark(item, _crosshair);
- if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionPolyFill))
- _macMenu->setCheckMark(item, !_wireframe);
- if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionCursorShoot))
+ if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuOptions, kMenuActionPolyFill))
+ _macMenu->setCheckMark(item, _wireframe);
+ if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuOptions, kMenuActionCursorShoot))
_macMenu->setCheckMark(item, _cursorShoot);
}
@@ -541,29 +557,43 @@ Common::Point ColonyEngine::getAimPoint() const {
void ColonyEngine::handleMenuAction(int action) {
switch (action) {
case kMenuActionAbout:
- inform("The Colony\nCopyright 1988\nDavid A. Smith", true);
+ runMacAbout();
break;
- case kMenuActionNew:
- startNewGame();
+ // gmain.c DoCommand: New, Open and Quit all go through QSave() first, so
+ // an unsaved game is never dropped on the floor. Yes saves and falls
+ // through to the action, No goes straight to it, Cancel does nothing.
+ case kMenuActionNew: {
+ const int answer = runMacSaveQuery();
+ if (answer == 1)
+ showSaveDialog();
+ if (answer)
+ startNewGame();
break;
- case kMenuActionOpen:
- _system->lockMouse(false);
- CursorMan.setDefaultArrowCursor();
- CursorMan.showMouse(true);
- loadGameDialog();
- updateMouseCapture(true);
+ }
+ case kMenuActionOpen: {
+ const int answer = runMacSaveQuery();
+ if (answer == 1)
+ showSaveDialog();
+ if (answer) {
+ _system->lockMouse(false);
+ CursorMan.setDefaultArrowCursor();
+ CursorMan.showMouse(true);
+ loadGameDialog();
+ updateMouseCapture(true);
+ }
break;
+ }
case kMenuActionSave:
case kMenuActionSaveAs:
- _system->lockMouse(false);
- CursorMan.setDefaultArrowCursor();
- CursorMan.showMouse(true);
- saveGameDialog();
- updateMouseCapture(true);
+ showSaveDialog();
break;
- case kMenuActionQuit:
- quitGame();
+ case kMenuActionQuit: {
+ const int answer = runMacSaveQuery();
+ // Quit only if the save went through, matching "if(saved)notDone=FALSE".
+ if (answer == 2 || (answer == 1 && showSaveDialog()))
+ quitGame();
break;
+ }
case kMenuActionSound:
_soundOn = !_soundOn;
if (!_soundOn)
@@ -618,41 +648,41 @@ void ColonyEngine::initMacMenus() {
_macMenu = _wm->addMenu();
_macMenu->setCommandsCallback(menuCommandsCallback, this);
- // Build menus matching original Mac Colony (inits.c lines 43-53, gmain.c DoCommand).
- // addStaticMenus() auto-adds the Apple menu at index 0, so:
- // index 0 = Apple, 1 = File, 2 = Edit, 3 = Options
- // NOTE: menunum=0 is the loop terminator, so Apple submenu items
- // must be added manually after addStaticMenus() (see WAGE pattern).
+ // Item text, order and shortcuts come from the Colony resource fork:
+ // MENU 1 (Apple), 256 (File), 257 (Edit), 258 (Options). The Color Colony
+ // fork renames these ("New Game", ...) and adds Color Palette / Load Color
+ // / Save Color for the unported Colorize() editor, so we follow the B&W
+ // fork, which is the app this engine actually is.
+ // addStaticMenus() auto-adds the Apple menu at index 0, and menunum=0 is
+ // its loop terminator, so Apple items are added by hand afterwards.
const Graphics::MacMenuData menuItems[] = {
{-1, "File", 0, 0, true},
{-1, "Edit", 0, 0, true},
{-1, "Options", 0, 0, true},
- // File submenu (index 1)
- {1, "New Game", kMenuActionNew, 'N', true},
- {1, "Open Game...", kMenuActionOpen, 'O', true},
- {1, "Save Game", kMenuActionSave, 'S', true},
- {1, "Save As...", kMenuActionSaveAs, 0, true},
- {1, nullptr, 0, 0, false}, // separator
- {1, "Quit", kMenuActionQuit, 'Q', true},
- // Edit submenu (index 2, disabled original Mac had these but non-functional)
- {2, "Undo", 0, 'Z', false},
- {2, nullptr, 0, 0, false},
- {2, "Cut", 0, 'X', false},
- {2, "Copy", 0, 'C', false},
- {2, "Paste", 0, 'V', false},
- // Options submenu (index 3)
- {3, "Sound", kMenuActionSound, 0, true},
- {3, "Crosshair", kMenuActionCrosshair, 0, true},
- {3, "Polygon Fill", kMenuActionPolyFill, 0, true},
- {3, "Cursor Shoot", kMenuActionCursorShoot, 0, true},
- // Terminator
+ {kMenuFile, "New", kMenuActionNew, 'N', true},
+ {kMenuFile, "Open", kMenuActionOpen, 'O', true},
+ {kMenuFile, "Save", kMenuActionSave, 'S', true},
+ {kMenuFile, "Save as ...", kMenuActionSaveAs, 0, true},
+ {kMenuFile, nullptr, 0, 0, false}, // separator
+ {kMenuFile, "Quit", kMenuActionQuit, 'Q', true},
+ // MENU 257 has these enabled, but DoCommand routes them to SystemEdit()
+ // and gmain.c flags it "This does'nt seem to work..." â keep them dead.
+ {kMenuEdit, "Undo", 0, 'Z', false},
+ {kMenuEdit, nullptr, 0, 0, false},
+ {kMenuEdit, "Cut", 0, 'X', false},
+ {kMenuEdit, "Copy", 0, 'C', false},
+ {kMenuEdit, "Paste", 0, 'V', false},
+ {kMenuOptions, "Sound", kMenuActionSound, 0, true},
+ {kMenuOptions, "Crosshair", kMenuActionCrosshair, 0, true},
+ {kMenuOptions, "Faster", kMenuActionPolyFill, 'F', true},
+ {kMenuOptions, "Manual Targetting", kMenuActionCursorShoot, 0, true},
{0, nullptr, 0, 0, false}
};
_macMenu->addStaticMenus(menuItems);
- // Add Apple submenu item manually (menunum=0 can't go through addStaticMenus)
- _macMenu->addSubMenu(nullptr, 0);
- _macMenu->addMenuItem(_macMenu->getSubmenu(nullptr, 0), "About The Colony", kMenuActionAbout);
+ Graphics::MacMenuSubMenu *appleMenu = _macMenu->addSubMenu(nullptr, kMenuApple);
+ _macMenu->addMenuItem(appleMenu, Common::String("About..."), kMenuActionAbout);
+ _macMenu->addMenuItem(appleMenu, Common::String(), 0, 0, 0, false); // separator
_macMenu->calcDimensions();
syncMacMenuChecks();
@@ -1255,6 +1285,31 @@ bool ColonyEngine::checkSkipRequested() {
return shouldQuit();
}
+bool ColonyEngine::checkClickRequested() {
+ // stars.c: with btn set, makestars() runs until Button(). Same event
+ // draining as checkSkipRequested(), but a plain click ends it.
+ Common::Event event;
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_RBUTTONDOWN:
+ return true;
+ case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+ if (event.customType == kActionEscape || event.customType == kActionFire)
+ return true;
+ break;
+ case Common::EVENT_SCREEN_CHANGED:
+ _gfx->computeScreenViewport();
+ break;
+ default:
+ break;
+ }
+ }
+ return shouldQuit();
+}
+
bool ColonyEngine::waitForInput() {
// Blocking wait for any key press or mouse click.
// Handles screen refresh, quit events, and mouse movement while waiting.
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 912ef8baf0f..5991129bd6a 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -47,6 +47,7 @@ class Cursor;
class Font;
class FrameLimiter;
class MacMenu;
+struct MacMenuItem;
class MacWindowManager;
class ManagedSurface;
}
@@ -303,6 +304,14 @@ enum MenuAction {
kMenuActionCursorShoot
};
+// Menu bar order, matching inits.c:43-48 (myMenus[0..3]).
+enum MenuIndex {
+ kMenuApple = 0,
+ kMenuFile,
+ kMenuEdit,
+ kMenuOptions
+};
+
static const int kBaseObject = 20;
static const int kMeNum = 101;
@@ -473,6 +482,7 @@ public:
void cCommand(int xnew, int ynew, bool allowInteraction);
bool scrollInfo(const Graphics::Font *macFont = nullptr);
bool checkSkipRequested();
+ bool checkClickRequested();
bool waitForInput();
void checkCenter();
void fallThroughHole();
@@ -483,6 +493,8 @@ public:
void printMessage(const char *text[], bool hold);
void makeMessageRect(Common::Rect &r);
int runMacEndgameDialog(const Common::String &message);
+ int runMacSaveQuery();
+ void runMacAbout();
private:
const ADGameDescription *_gameDescription;
@@ -611,6 +623,9 @@ private:
void loadMacCursorResources();
void handleMenuAction(int action);
static void menuCommandsCallback(int action, Common::String &text, void *data);
+ // getSubMenuItem() indexes a submenu; our ids are actions. Look up by action.
+ Graphics::MacMenuItem *macMenuItemForAction(int menuIndex, int action);
+ bool showSaveDialog();
int _frntxWall = 0, _frntyWall = 0;
int _sidexWall = 0, _sideyWall = 0;
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 1a36afc849b..10ad089ce5f 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -129,6 +129,107 @@ public:
}
};
+void ColonyEngine::runMacAbout() {
+ // gmain.c About(): a black full-screen window with three click-through
+ // starfield stages â empty, the title PICT, then the credits. about0[]
+ // ("The Colony" / "By David A. Smith" / "Copyright \xA9 1989") is declared
+ // alongside but never drawn; PICT -32564 carries that text.
+ if (!isMacRenderMode() || !_gfx)
+ return;
+
+ if (_macMenu && _wm && _wm->isMenuActive())
+ _macMenu->closeMenu();
+
+ const char *credits[] = {
+ "Special thanks to",
+ "Mike Kahl & THINK'S LightspeedC\xA9",
+ "Symantec Corporation"
+ };
+
+ const Common::Rect r(_width, _height);
+ _system->lockMouse(false);
+ CursorMan.showMouse(false);
+
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ makeStars(r, 1);
+
+ _gfx->clear(_gfx->black());
+ if (!drawPict(-32564)) // Color Colony
+ drawPict(-32750); // B&W Colony
+ makeStars(r, 1);
+
+ _gfx->clear(_gfx->black());
+ Graphics::MacFont systemFont(Graphics::kMacFontSystem, 12);
+ const Graphics::Font *font = _wm && _wm->_fontMan ? _wm->_fontMan->getFont(systemFont) : nullptr;
+ if (font) {
+ for (int i = 0; i < ARRAYSIZE(credits); i++)
+ _gfx->drawString(font, credits[i], _width / 2, (_height / 2 - 20) + 20 * i,
+ _gfx->white(), Graphics::kTextAlignCenter);
+ }
+ _gfx->copyToScreen();
+ makeStars(r, 1);
+
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ updateMouseCapture(true);
+}
+
+int ColonyEngine::runMacSaveQuery() {
+ // gmain.c QSave(2002): DLOG/DITL 2002, "Save this game?" Yes/No/Cancel.
+ // Returns 1 = save then proceed, 2 = proceed, 0 = cancel (QSave maps
+ // Cancel to 0, which matches none of the caller's cases).
+ if (!isMacRenderMode() || !_wm || !_menuSurface || !_gfx)
+ return 2;
+
+ if (_macMenu && _wm->isMenuActive())
+ _macMenu->closeMenu();
+
+ // Unlike the endgame dialog we keep the game frame behind the prompt, so
+ // clear to transparent rather than black and put the menu bar back.
+ _menuSurface->fillRect(Common::Rect(0, 0, _menuSurface->w, _menuSurface->h),
+ _menuSurface->format.ARGBToColor(0, 0, 0, 0));
+ if (_macMenu)
+ _macMenu->draw(_menuSurface, true);
+
+ const Common::String yesLabel = _("Yes");
+ const Common::String noLabel = _("No");
+ const Common::String cancelLabel = _("Cancel");
+ Graphics::MacFont systemFont(Graphics::kMacFontSystem, 12);
+ const Graphics::Font *dialogFont = (_wm->_fontMan) ? _wm->_fontMan->getFont(systemFont) : nullptr;
+
+ const int buttonGap = 12;
+ const int buttonH = 28;
+ const int buttonPad = 26;
+ const int minButtonW = 68;
+ const int buttonW1 = MAX<int>(minButtonW, dialogFont ? dialogFont->getStringWidth(yesLabel) + buttonPad : 80);
+ const int buttonW2 = MAX<int>(minButtonW, dialogFont ? dialogFont->getStringWidth(noLabel) + buttonPad : 80);
+ const int buttonW3 = MAX<int>(minButtonW, dialogFont ? dialogFont->getStringWidth(cancelLabel) + buttonPad : 80);
+ const int totalButtonsW = buttonW1 + buttonW2 + buttonW3 + buttonGap * 2;
+ const int maxTextWidth = CLIP<int>(_width - 48, 180, 280);
+
+ Graphics::MacText prompt(Common::U32String(_("Save this game?")), _wm, &systemFont,
+ _wm->_colorBlack, _wm->_colorWhite, maxTextWidth, Graphics::kTextAlignCenter);
+
+ const int dialogW = MAX<int>(MAX<int>(220, totalButtonsW + 20), maxTextWidth + 20);
+ const int buttonY = prompt.getTextHeight() + 30;
+ const int startX = (dialogW - totalButtonsW) / 2;
+ Graphics::MacDialogButtonArray buttons;
+ buttons.push_back(new Graphics::MacDialogButton(yesLabel.c_str(), startX, buttonY, buttonW1, buttonH));
+ buttons.push_back(new Graphics::MacDialogButton(noLabel.c_str(), startX + buttonW1 + buttonGap, buttonY, buttonW2, buttonH));
+ buttons.push_back(new Graphics::MacDialogButton(cancelLabel.c_str(), startX + buttonW1 + buttonGap + buttonW2 + buttonGap, buttonY, buttonW3, buttonH));
+
+ ColonyMacDialog dialog(_menuSurface, _wm, dialogW, &prompt, maxTextWidth, &buttons, 0);
+ switch (dialog.runWithRenderer(_gfx)) {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ default: // Cancel, ESC or quit
+ return 0;
+ }
+}
+
int ColonyEngine::runMacEndgameDialog(const Common::String &message) {
if (!isMacRenderMode() || !_wm || !_menuSurface || !_gfx)
return Graphics::kMacDialogQuitRequested;
@@ -638,9 +739,14 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
}
_gfx->copyToScreen();
- // Animate: original loops ~200 frames or until Mars sound repeats 2x
- for (int k = 0; k < 120; k++) {
- if (checkSkipRequested()) {
+ // Animate: original loops ~200 frames or until Mars sound repeats 2x.
+ // With btn set (About) it streaks on until the user clicks, and unlike the
+ // skip path that click still falls through to the fade-out below.
+ for (int k = 0; btn || k < 120; k++) {
+ if (btn) {
+ if (checkClickRequested())
+ break;
+ } else if (checkSkipRequested()) {
_gfx->setXorMode(false);
return true;
}
Commit: 80ee54d155446d405c067bebf81cb9f3cdb2e4ba
https://github.com/scummvm/scummvm/commit/80ee54d155446d405c067bebf81cb9f3cdb2e4ba
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-30T12:40:44+02:00
Commit Message:
SCUMM: RA2: marked as rebel2-psx as unstable
Changed paths:
engines/scumm/detection.cpp
engines/scumm/detection.h
engines/scumm/detection_tables.h
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 2d43fd184b6..527fcd73a3d 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -241,7 +241,9 @@ DetectedGames ScummMetaEngineDetection::detectGames(const Common::FSList &fslist
game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
game.appendGUIOptions(getGameGUIOptionsDescriptionPlatform(x->game.platform));
- if (x->game.features & GF_TESTING)
+ if (x->game.features & GF_UNSTABLE)
+ game.gameSupportLevel = kUnstableGame;
+ else if (x->game.features & GF_TESTING)
game.gameSupportLevel = kTestingGame;
detectedGames.push_back(game);
diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index 9869398f735..8f5d11f0dc6 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -159,6 +159,9 @@ enum GameFeatures {
/** Games with the AKOS costume system (ScummEngine_v7 and subclasses, HE games). */
GF_NEW_COSTUMES = 1 << 2,
+ /** Games which are not even ready for public testing yet. */
+ GF_UNSTABLE = 1 << 3,
+
/** Games using XOR encrypted data files. */
GF_USE_KEY = 1 << 4,
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 19b4cb535b3..3f3977bc632 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -233,8 +233,8 @@ static const GameSettings gameVariantsTable[] = {
{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, GF_TESTING, Common::kPlatformDOS, GUIO3(GUIO_NOMIDI, GAMEOPTION_REBEL1_UNLOCK_ALL, GAMEOPTION_REBEL1_NO_DAMAGE)},
{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_TESTING, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
- {"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
- {"rebel2", "PlayStation", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_TESTING | GF_16BIT_COLOR, Common::kPlatformPSX, GUIO5(GUIO_NOMIDI, GUIO_NOASPECT, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
+ {"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO | GF_TESTING, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
+ {"rebel2", "PlayStation", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_UNSTABLE | GF_16BIT_COLOR, Common::kPlatformPSX, GUIO5(GUIO_NOMIDI, GUIO_NOASPECT, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
{"dig", "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO5(GUIO_NOMIDI, GAMEOPTION_ENHANCEMENTS, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
{"dig", "Demo", 0, GID_DIG, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
More information about the Scummvm-git-logs
mailing list