瀏覽代碼

The installer parses all worlds and generates world scripts.

Iñigo Valentin 1 周之前
父節點
當前提交
6741b6cb36

+ 109 - 107
data/data/scripts/menu/begin_menu.lua

@@ -1,31 +1,31 @@
 if UiContainer == nil then UiContainer = {} end
 
---- The begin menu
-UiContainer.BeginMenu = {
+    --- The begin menu
+    UiContainer.BeginMenu = {
 
-    --- Current cursor position
-    position = 1,
+        --- Current cursor position
+        position = 1,
 
-    -- Max cursor position
-    position_total = 7,
+        -- Max cursor position
+        position_total = 7,
 
-    --- Run when the menu is created.
-    --
-    -- Sets this menu as the one beind displayed.
-    on_start = function(self)
+        --- Run when the menu is created.
+        --
+        -- Sets this menu as the one beind displayed.
+        on_start = function(self)
         UiContainer.current_menu = "begin"
         local cursor = ui_manager:get_widget("BeginMenu.Container.Cursor")
         cursor:set_default_animation("Position" .. self.position)
         return 0
-    end,
+        end,
 
 
-    --- Handles button events.
-    --
-    -- Handles directional Up, Down and Enter keys.
-    -- @param button Pressed button string key. Config.CONTROLS.UP, Config.CONTROLS.LEFT, Config.CONTROLS.ACTION and Config.CONTROLS.CANCEL are handled.
-    -- @param event Trigger event. Normally, "Press".
-    on_button = function(self, button, event)
+        --- Handles button events.
+        --
+        -- Handles directional Up, Down and Enter keys.
+        -- @param button Pressed button string key. Config.CONTROLS.UP, Config.CONTROLS.LEFT, Config.CONTROLS.ACTION and Config.CONTROLS.CANCEL are handled.
+        -- @param event Trigger event. Normally, "Press".
+        on_button = function(self, button, event)
         if UiContainer.current_menu == "begin" then
             local cursor = ui_manager:get_widget("BeginMenu.Container.Cursor")
             if button == Config.CONTROLS.ACTION and event == "Press" then
@@ -37,103 +37,105 @@ UiContainer.BeginMenu = {
                     --console("debug_walkmesh true")
                     script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
                     MenuSettings.pause_available = true
-                elseif self.position == 2 then -- Load, show save menu.
-                    audio_manager:play_sound("Cursor")
-                    UiContainer.SaveMenu.operation = "load"
-                    script:request_end_sync(Script.UI, "SaveMenu", "show", 0)
-                elseif self.position == 3 then
-                    -- NMKIN_3 START
-                    Data.progress_game = 27
-                    load_field_map_request("nmkin_3", "")
-                    console("camera_free false")
-                    console("debug_walkmesh true")
-                    script:wait(1)
-                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
-                    entity_manager:get_entity("Cloud"):set_position(-1.090750, 15.310307, 17.281244)
-                    entity_manager:set_player_entity("Cloud")
-                    script:wait(1)
-                    entity_manager:get_player_entity():set_position(-1.093750, 15.320307, 17.281244)
-                    entity_manager:get_entity("Cloud"):jump_to_position(0.46875, 14.9922, -1, 1, 42) -- Triangle 42, 2 steps.
-                    entity_manager:get_entity("Cloud"):jump_sync()
-                    entity_manager:player_lock(false)
-                    MenuSettings.pause_available = true
-                    -- NMKIN_3 END
-                    -- NMKIN_4 START
-                    --[[Data.progress_game = 14
-                    --load_field_map_request("nmkin_4", "")
-                    map("nmkin_4")
-                    console("camera_free false")
-                    console("debug_walkmesh true")
-                    script:wait(1.5)
-                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
-                    entity_manager:get_entity("Cloud"):set_position(-0.820312, 0.500000, 17.281244)
-                    entity_manager:set_player_entity("Cloud")
-                    set_party(0, 1, 4)
-                    script:wait(0.5)
-                    entity_manager:get_player_entity():set_position(-0.820312, 0.500000, 17.281244)
-                    entity_manager:get_entity("Cloud"):jump_to_position(-0.800312, 0.500000, -1, 1) -- Triangle 42, 2 steps.
-                    entity_manager:get_entity("Cloud"):jump_sync()
-                    entity_manager:player_lock(false)
-                    print("TRIANGLE " .. entity_manager:get_entity("Cloud"):get_move_triangle_id())
-                    MenuSettings.pause_available = true]]
-                    -- NMKIN_4 END
-                elseif self.position == 4 then
-                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
-                    console("camera_free true")
-                    console("debug_walkmesh true")
-                    map("test_2")
-                    MenuSettings.pause_available = true
-                elseif self.position == 5 then
-                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
-                    console("camera_free true")
-                    world_map_module:init()
-                elseif self.position == 6 then
-                    load_field_map_request("startmap", "")
-                    console("camera_free false")
-                    console("debug_walkmesh false")
-                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
-                    MenuSettings.pause_available = true
-                elseif self.position == 7 then
-                    --load_field_map_request("startmap", "")
-                    console("camera_free false")
-                    console("debug_walkmesh false")
-                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
-                    set_party(0, 2, 3)
-                    Battle.start(220)
-                    MenuSettings.pause_available = true
-                end
-            elseif button == Config.CONTROLS.DOWN then
-                audio_manager:play_sound("Cursor")
-                self.position = self.position + 1
-                if self.position > self.position_total then
-                    self.position = 1;
-                end
-                cursor:set_default_animation("Position" .. self.position)
-            elseif button == Config.CONTROLS.UP then
-                audio_manager:play_sound("Cursor")
-                self.position = self.position - 1
-                if self.position <= 0 then
-                    self.position = self.position_total;
-                end
-                cursor:set_default_animation("Position" .. self.position)
-            end
+                    elseif self.position == 2 then -- Load, show save menu.
+                        audio_manager:play_sound("Cursor")
+                        UiContainer.SaveMenu.operation = "load"
+                        script:request_end_sync(Script.UI, "SaveMenu", "show", 0)
+                        elseif self.position == 3 then
+                            -- NMKIN_3 START
+                            Data.progress_game = 27
+                            load_field_map_request("nmkin_3", "")
+                            console("camera_free false")
+                            console("debug_walkmesh true")
+                            script:wait(1)
+                            script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
+                            entity_manager:get_entity("Cloud"):set_position(-1.090750, 15.310307, 17.281244)
+                            entity_manager:set_player_entity("Cloud")
+                            script:wait(1)
+                            entity_manager:get_player_entity():set_position(-1.093750, 15.320307, 17.281244)
+                            entity_manager:get_entity("Cloud"):jump_to_position(0.46875, 14.9922, -1, 1, 42) -- Triangle 42, 2 steps.
+                            entity_manager:get_entity("Cloud"):jump_sync()
+                            entity_manager:player_lock(false)
+                            MenuSettings.pause_available = true
+                            -- NMKIN_3 END
+                            -- NMKIN_4 START
+                            --[[Data.progress_game = 14
+                            --load_field_map_request("nmkin_4", "")
+                            map("nmkin_4")
+                            console("camera_free false")
+                            console("debug_walkmesh true")
+                            script:wait(1.5)
+                            script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
+                            entity_manager:get_entity("Cloud"):set_position(-0.820312, 0.500000, 17.281244)
+                            entity_manager:set_player_entity("Cloud")
+                            set_party(0, 1, 4)
+                            script:wait(0.5)
+                            entity_manager:get_player_entity():set_position(-0.820312, 0.500000, 17.281244)
+                            entity_manager:get_entity("Cloud"):jump_to_position(-0.800312, 0.500000, -1, 1) -- Triangle 42, 2 steps.
+                            entity_manager:get_entity("Cloud"):jump_sync()
+                            entity_manager:player_lock(false)
+                            print("TRIANGLE " .. entity_manager:get_entity("Cloud"):get_move_triangle_id())
+                            MenuSettings.pause_available = true]]
+                            -- NMKIN_4 END
+                            elseif self.position == 4 then
+                                script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
+                                console("camera_free true")
+                                console("debug_walkmesh true")
+                                map("test_2")
+                                MenuSettings.pause_available = true
+                                elseif self.position == 5 then
+                                    script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
+                                    console("camera_free true")
+                                    console("debug_entity 2")
+                                    MenuSettings.pause_available = true
+                                    world_map_module:init()
+                                    elseif self.position == 6 then
+                                        load_field_map_request("startmap", "")
+                                        console("camera_free false")
+                                        console("debug_walkmesh false")
+                                        script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
+                                        MenuSettings.pause_available = true
+                                        elseif self.position == 7 then
+                                            --load_field_map_request("startmap", "")
+                                            console("camera_free false")
+                                            console("debug_walkmesh false")
+                                            script:request_end_sync(Script.UI, "BeginMenu", "hide", 0)
+                                            set_party(0, 2, 3)
+                                            Battle.start(220)
+                                            MenuSettings.pause_available = true
+                                            end
+                                            elseif button == Config.CONTROLS.DOWN then
+                                                audio_manager:play_sound("Cursor")
+                                                self.position = self.position + 1
+                                                if self.position > self.position_total then
+                                                    self.position = 1;
+        end
+        cursor:set_default_animation("Position" .. self.position)
+        elseif button == Config.CONTROLS.UP then
+            audio_manager:play_sound("Cursor")
+            self.position = self.position - 1
+            if self.position <= 0 then
+                self.position = self.position_total;
+        end
+        cursor:set_default_animation("Position" .. self.position)
+        end
         end
         return 0
-    end,
+        end,
 
-    --- Opens the save menu.
-    show = function(self)
+        --- Opens the save menu.
+        show = function(self)
         UiContainer.current_menu = "begin"
         ui_manager:get_widget("BeginMenu"):set_visible(true)
         return 0;
-    end,
+        end,
 
-    --- Hiddens the begin menu.
-    --
-    -- It doesn't do anything else.
-    hide = function(self)
+        --- Hiddens the begin menu.
+        --
+        -- It doesn't do anything else.
+        hide = function(self)
         UiContainer.current_menu = ""
         ui_manager:get_widget("BeginMenu"):set_visible(false)
         return 0;
-    end,
-}
+        end,
+    }

+ 2 - 2
data/data/scripts/worldmap/vehicles.lua

@@ -148,7 +148,7 @@ end
 EntityContainer["WorldMapBuggy"] = extend_vehicle(
     "WorldMapBuggy",
     {
-        "GRASS", "FOREST", "SWAMP", "DESERT", "WASTELAND", "SNOW", "RIVERSIDE", "CLIFF",
+        "GRASS", "FOREST", "SWAMP", "DESERT", "WASTELAND", "SNOW", "RIVERSIDE",
         "COREL_BRIDGE", "WUTAI_BRIDGE", "HILL_SIDE", "BEACH", "RIVER_CROSSING", "CANYON",
         "MOUNTAIN_PASS", "GOLD_SAUCER_DESERT", "JUNGLE", "GOLD_SAUCER_DESERT_BORDER",
         "BRIDGE_HEAD", "BACK_ENTRANCE",
@@ -285,7 +285,7 @@ end
 EntityContainer["WorldMapTinyBronco"] = extend_vehicle(
     "WorldMapTinyBronco",
     {
-        "RIVERSIDE", "CLIFF", "BEACH", "RIVER_CROSSING", "RIVER", "WATER",
+        "RIVERSIDE", "BEACH", "RIVER_CROSSING", "RIVER", "WATER",
     },
     0,
     {"BEACH", "RIVERSIDE"}

+ 7 - 3
src/installer/DataInstaller.cpp

@@ -72,7 +72,11 @@ float DataInstaller::Progress(){
             media_installer_ = std::make_unique<MediaDataInstaller>(
               disk_image_, output_dir_, options_.keep_originals, options_.ffmpeg, options_.timidity
             );
-            field_installer_ = std::make_unique<FieldDataInstaller>(disk_image_, output_dir_);
+                        field_installer_ = std::make_unique<FieldDataInstaller>(
+                            disk_image_, output_dir_, [this](std::string log_message){
+                                    write_output_line_(log_message, 2, true);
+                            }
+                        );
             battle_installer_ = std::make_unique<BattleDataInstaller>(disk_image_, output_dir_, application_.ResMgr());
             world_installer_ = std::make_unique<WorldInstaller>(
               disk_image_, output_dir_, options_.keep_originals, application_.ResMgr()
@@ -417,10 +421,10 @@ float DataInstaller::Progress(){
                                         field_installer_->GetMapList(), field_installer_->GetFieldScaleFactors(),
                                         field_installer_->GetConvertedMapList(), script_error
                                     )){
-                    write_output_line_("Generated WM0 world-map script.", 2, true);
+                    write_output_line_("Generated world-map scripts.", 2, true);
                 }
                 else{
-                    write_output_line_("Could not generate WM0 world-map script: " + script_error, 2, true);
+                    write_output_line_("Could not generate world-map scripts: " + script_error, 2, true);
                 }
                 installation_state_ = WM_MODELS;
                 cur_substep_ = 0;

+ 6 - 2
src/installer/FieldDataInstaller.cpp

@@ -216,8 +216,12 @@ bool FieldDataInstaller::CollectSpawnPoints(
     return true;
 }
 
-FieldDataInstaller::FieldDataInstaller(const DiskImage& disk_image, const std::string output_dir):
-  disk_image_(disk_image), output_dir_(output_dir)
+FieldDataInstaller::FieldDataInstaller(
+    const DiskImage& disk_image, const std::string output_dir,
+    std::function<void(std::string)> log_output_line
+):
+    disk_image_(disk_image), output_dir_(output_dir),
+    write_output_line_(log_output_line)
 {}
 
 FieldDataInstaller::~FieldDataInstaller(){}

+ 4 - 1
src/installer/FieldDataInstaller.h

@@ -307,7 +307,10 @@ class FieldDataInstaller{
          * @param[in] output_dir Path to the directory to write generated data
          * to.
          */
-        FieldDataInstaller(const DiskImage& disk_image, const std::string output_dir);
+        FieldDataInstaller(
+          const DiskImage& disk_image, const std::string output_dir,
+          std::function<void(std::string)> log_output_line
+        );
 
         /**
          * Installer destructor.

+ 64 - 42
src/installer/WorldInstaller.cpp

@@ -672,52 +672,72 @@ bool WorldInstaller::GenerateScripts(
     archive.open(*archive_path, true);
     archive.load();
     const VGears::LGPArchive::FileList& archive_entries = archive.GetFiles();
-    const VGears::LGPArchive::FileEntry* event_entry = nullptr;
+    std::vector<const VGears::LGPArchive::FileEntry*> event_entries;
     const VGears::LGPArchive::FileEntry* field_table_entry = nullptr;
+    bool has_wm0 = false;
     for (const VGears::LGPArchive::FileEntry& entry : archive_entries){
-        if (entry.file_name == "wm0.ev" || entry.file_name == "WM0.EV"){
-            event_entry = &entry;
+        std::string lower_name = entry.file_name;
+        VGears::StringUtil::toLowerCase(lower_name);
+        if (
+          lower_name.size() > 5 && lower_name.compare(0, 2, "wm") == 0
+          && lower_name.compare(lower_name.size() - 3, 3, ".ev") == 0
+        ){
+            const std::string map_number
+              = lower_name.substr(2, lower_name.size() - 5);
+            if (!map_number.empty() && std::all_of(
+                  map_number.begin(), map_number.end(), [](char character){
+                      return character >= '0' && character <= '9';
+                  }
+                )){
+                event_entries.push_back(&entry);
+                if (lower_name == "wm0.ev") has_wm0 = true;
+            }
         }
-        else if (entry.file_name == "field.tbl" || entry.file_name == "FIELD.TBL")
+        else if (lower_name == "field.tbl"){
             field_table_entry = &entry;
+        }
     }
-    if (event_entry == nullptr || field_table_entry == nullptr){
-        error_message = "WM0.EV or FIELD.TBL was not found inside WORLD_US.LGP.";
+    if (event_entries.empty() || !has_wm0 || field_table_entry == nullptr){
+        error_message = "WM0.EV, other world event files, or FIELD.TBL were not found inside WORLD_US.LGP.";
         return false;
     }
 
-    File event_file(&archive_file, event_entry->data_offset, event_entry->data_size);
-    std::vector<u8> event_data(event_entry->data_size);
-    if (event_data.empty()){
-        error_message = "WM0.EV inside WORLD_US.LGP is empty.";
+    File field_table_file(
+      &archive_file, field_table_entry->data_offset, field_table_entry->data_size
+    );
+    std::vector<u8> field_table_data(field_table_entry->data_size);
+    if (field_table_data.size() != 64 * 24){
+        error_message = "FIELD.TBL has an unexpected size.";
         return false;
     }
-    event_file.GetFileBuffer(event_data.data(), 0, event_entry->data_size);
-
-        File field_table_file(
-            &archive_file, field_table_entry->data_offset, field_table_entry->data_size
-        );
-        std::vector<u8> field_table_data(field_table_entry->data_size);
-        if (field_table_data.size() != 64 * 24){
-                error_message = "FIELD.TBL has an unexpected size.";
-                return false;
-        }
-        field_table_file.GetFileBuffer(
-            field_table_data.data(), 0, field_table_entry->data_size
-        );
+    field_table_file.GetFileBuffer(
+      field_table_data.data(), 0, field_table_entry->data_size
+    );
 
     const std::string script_directory = output_dir_ + "/scripts/worldmap";
     boost::filesystem::create_directories(script_directory);
-    std::ofstream script_file(script_directory + "/wm0.lua");
-    if (!script_file){
-        error_message = "Cannot create script output in " + script_directory + ".";
-        return false;
-    }
-    script_file << "-- Decompiled from the user's WM0.EV data.\n\n";
+    for (const VGears::LGPArchive::FileEntry* event_entry : event_entries){
+        File event_file(&archive_file, event_entry->data_offset, event_entry->data_size);
+        std::vector<u8> event_data(event_entry->data_size);
+        if (event_data.empty()){
+            error_message = event_entry->file_name + " inside WORLD_US.LGP is empty.";
+            return false;
+        }
+        event_file.GetFileBuffer(event_data.data(), 0, event_entry->data_size);
+
+        std::string map_file_name = event_entry->file_name;
+        VGears::StringUtil::toLowerCase(map_file_name);
+        const std::string map_name = map_file_name.substr(0, map_file_name.size() - 3);
+        std::ofstream script_file(script_directory + "/" + map_name + ".lua");
+        if (!script_file){
+            error_message = "Cannot create script output in " + script_directory + ".";
+            return false;
+        }
+        script_file << "-- Decompiled from the user's " << event_entry->file_name << " data.\n\n";
 
-    std::vector<bool> generated_functions(256, false);
-    unsigned int generated_function_count = 0;
-    for (int script_index = 0; script_index < 256; ++script_index){
+        std::vector<bool> generated_functions(256, false);
+        unsigned int generated_function_count = 0;
+        for (int script_index = 0; script_index < 256; ++script_index){
         try{
             WorldEngine engine(script_index);
             InstVec instructions;
@@ -744,13 +764,14 @@ bool WorldInstaller::GenerateScripts(
             script_file << "-- world function " << script_index
               << " failed: " << exception.what() << ".\n";
         }
-    }
+        }
 
-        script_file << "-- WM0 mesh-function call-table mapping.\n";
+        script_file << "-- " << map_name << " mesh-function call-table mapping.\n";
         for (size_t function_index = 0; function_index < generated_functions.size(); ++function_index){
                 if (!generated_functions[function_index]) continue;
 
                 const size_t type_offset = function_index * 4;
+            if (type_offset + 1 >= event_data.size()) continue;
                 const uint16 function_type = static_cast<uint16>(event_data[type_offset])
                     | static_cast<uint16>(event_data[type_offset + 1] << 8);
                 if ((function_type >> 14) != 2) continue;
@@ -804,14 +825,15 @@ bool WorldInstaller::GenerateScripts(
                             << direction_degrees << "}\n";
                 }
         }
-    script_file.flush();
-    if (!script_file){
-        error_message = "Failed while writing " + script_directory + "/wm0.lua.";
-        return false;
-    }
-    if (generated_function_count == 0){
-        error_message = "No WM0.EV functions could be decompiled.";
-        return false;
+        script_file.flush();
+        if (!script_file){
+            error_message = "Failed while writing " + script_directory + "/" + map_name + ".lua.";
+            return false;
+        }
+        if (generated_function_count == 0){
+            error_message = "No functions in " + event_entry->file_name + " could be decompiled.";
+            return false;
+        }
     }
     return true;
 }

+ 9 - 1
src/installer/decompiler/world/instruction/WorldSubStackInstruction.cpp

@@ -48,6 +48,14 @@ void WorldSubStackInstruction::ProcessInst(
         case 0x51: op = "<<"; break;
         default:op = "unknown_operation";
     }
-    std::string value = stack.Pop()->GetString() + " " + op + " " + stack.Pop()->GetString();
+    std::string value;
+    if (opcode_ == 0x51){
+        const std::string left = stack.Pop()->GetString();
+        const std::string right = stack.Pop()->GetString();
+        value = "((" + left + ") * (2 ^ " + right + "))";
+    }
+    else{
+        value = stack.Pop()->GetString() + " " + op + " " + stack.Pop()->GetString();
+    }
     stack.Push(new VarValue(value));
 }

+ 1 - 5
src/modules/worldmap/WorldmapModule.cpp

@@ -1232,11 +1232,7 @@ namespace VGears {
       EntityManager::getSingleton().SetWorldModule();
         DialogsManager::getSingleton().SetWorldModule();
         CreateWorldMap(Root::getSingleton().getSceneManager("Scene"));
-        if (ResourceGroupManager::getSingleton().resourceExists(
-              "SCRIPTS", "worldmap/wm0.lua"
-            )){
-            ScriptManager::getSingleton().RunFile("scripts/worldmap/wm0.lua");
-        }
+        ScriptManager::getSingleton().RunFile("scripts/worldmap/wm0.lua");
         CreatePlayer();
         initialized_ = true;
         Root::getSingleton().addFrameListener(this);

+ 12 - 5
test/installer/decompiler/world/instruction/WorldSubStackInstruction.cpp

@@ -16,11 +16,18 @@
 #include <boost/test/unit_test.hpp>
 #include "installer/decompiler/world/instruction/WorldSubStackInstruction.h"
 
-#pragma message("Unimplemented test file: " __FILE__)
+BOOST_AUTO_TEST_CASE(TestWorldSubStackInstructionLeftShiftIsValidLua){
+    WorldSubStackInstruction instruction;
+    instruction.SetOpcode(0x51);
 
-BOOST_AUTO_TEST_CASE(TestWorldSubStackInstruction){
-    // TODO
-    BOOST_WARN_MESSAGE(false, "Unimplemented test file: WorldSubStackInstruction");
-    BOOST_CHECK(0 == 0);
+    ValueStack stack;
+    stack.Push(new VarValue("2"));
+    stack.Push(new VarValue("2928 - Read(912)"));
+    Function function;
+    instruction.ProcessInst(function, stack, nullptr, nullptr);
+
+    BOOST_CHECK_EQUAL(
+        stack.Pop()->GetString(), "((2928 - Read(912)) * (2 ^ 2))"
+    );
 }