/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://gnu.org/licenses */ #include "object/implementation/program_storage_impl.h" #include "common/global.h" #include "common/logger.h" #include "common/stringutils.h" #include "common/resources/resourcemanager.h" #include "level/robotmain.h" #include "level/parser/parserline.h" #include "math/all.h" #include "object/object.h" #include "object/old_object.h" #include "object/interface/controllable_object.h" #include "object/interface/task_executor_object.h" #include "object/motion/motion.h" #include "object/motion/motionvehicle.h" #include "physics/physics.h" #include "script/script.h" #include "ui/controls/edit.h" #include #include #include CProgramStorageObjectImpl::CProgramStorageObjectImpl(ObjectInterfaceTypes& types, CObject* object) : CProgramStorageObject(types), m_object(object), m_program(), m_activeVirus(false), m_programStorageIndex(-1), m_allowProgramSave(true) { } CProgramStorageObjectImpl::~CProgramStorageObjectImpl() {} bool CProgramStorageObjectImpl::IntroduceVirus() { if(m_program.size() == 0) return false; for ( int i=0 ; i<50 ; i++ ) { int programIndex = rand() % m_program.size(); if ( m_program[programIndex]->script->IntroduceVirus() ) // tries to introduce { m_program[programIndex]->filename = ""; // The program is changed, so force it to save instead of just the filename m_activeVirus = true; // active virus return true; } } return false; } void CProgramStorageObjectImpl::SetActiveVirus(bool bActive) { m_activeVirus = bActive; if ( !m_activeVirus ) // virus disabled? { m_object->SetVirusMode(false); // chtites (??? - Programerus) letters also } } bool CProgramStorageObjectImpl::GetActiveVirus() { return m_activeVirus; } Program* CProgramStorageObjectImpl::AddProgram() { assert(m_object->Implements(ObjectInterfaceType::Old)); //TODO auto program = std::make_unique(); program->script = std::make_unique(dynamic_cast(this)); Program* prog = program.get(); AddProgram(std::move(program)); return prog; } void CProgramStorageObjectImpl::AddProgram(std::unique_ptr program) { m_program.push_back(std::move(program)); m_object->UpdateInterface(); } void CProgramStorageObjectImpl::RemoveProgram(Program* program) { m_program.erase( std::remove_if(m_program.begin(), m_program.end(), [program](std::unique_ptr& prog) { return prog.get() == program; }), m_program.end()); m_object->UpdateInterface(); } Program* CProgramStorageObjectImpl::CloneProgram(Program* program) { Program* newprog = AddProgram(); // TODO: Is there any reason CScript doesn't have a function to get the program code directly? auto edit = std::make_unique(); program->script->PutScript(edit.get(), ""); newprog->script->GetScript(edit.get()); return newprog; } std::vector>& CProgramStorageObjectImpl::GetPrograms() { return m_program; } int CProgramStorageObjectImpl::GetProgramCount() { return static_cast(m_program.size()); } int CProgramStorageObjectImpl::GetProgramIndex(Program* program) { for(unsigned int i = 0; i < m_program.size(); i++) { if(m_program[i].get() == program) { return i; } } return -1; } Program* CProgramStorageObjectImpl::GetProgram(int index) { if(index < 0 || index >= static_cast(m_program.size())) return nullptr; return m_program[index].get(); } Program* CProgramStorageObjectImpl::GetOrAddProgram(int index) { if(index < 0) return nullptr; if(index < static_cast(m_program.size())) return m_program[index].get(); for(int i = m_program.size(); i < index; i++) { AddProgram(); } return AddProgram(); } bool CProgramStorageObjectImpl::ReadProgram(Program* program, const std::string& filename) { if ( program->script->ReadScript(filename.c_str()) ) return true; return false; } bool CProgramStorageObjectImpl::WriteProgram(Program* program, const std::string& filename) { if ( program->script->WriteScript(filename.c_str()) ) return true; return false; } bool CProgramStorageObjectImpl::GetCompile(Program* program) { return program->script->GetCompile(); } void CProgramStorageObjectImpl::SetProgramStorageIndex(int programStorageIndex) { m_programStorageIndex = programStorageIndex; } int CProgramStorageObjectImpl::GetProgramStorageIndex() { return m_programStorageIndex; } void CProgramStorageObjectImpl::SaveAllUserPrograms(const std::string& userSource) { if (!m_allowProgramSave) return; if (m_programStorageIndex < 0) return; GetLogger()->Debug("Saving user programs to '%s%.3d___.txt'\n", userSource.c_str(), m_programStorageIndex); for (unsigned int i = 0; i < m_program.size(); i++) { std::string filename = userSource + StrUtils::Format("%.3d%.3d.txt", m_programStorageIndex, i); if (m_program[i]->filename.empty()) { GetLogger()->Trace("Saving program '%s' into user directory\n", filename.c_str()); WriteProgram(m_program[i].get(), filename); } } std::string dir = userSource.substr(0, userSource.find_last_of("/")); std::string file = userSource.substr(userSource.find_last_of("/")+1) + StrUtils::Format("%.3d([0-9]{3})\\.txt", m_programStorageIndex); std::regex regex(file); for (const std::string& filename : CResourceManager::ListFiles(dir)) { std::smatch matches; if (std::regex_match(filename, matches, regex)) { unsigned int id = std::stoul(matches[1]); if (id >= m_program.size() || !m_program[id]->filename.empty()) { GetLogger()->Trace("Removing old program '%s/%s'\n", dir.c_str(), filename.c_str()); CResourceManager::Remove(dir+"/"+filename); } } } } void CProgramStorageObjectImpl::LoadAllProgramsForLevel(CLevelParserLine* levelSource, const std::string& userSource, bool loadSoluce) { int run = levelSource->GetParam("run")->AsInt(0)-1; bool allFilled = true; for (int i = 0; i < 10 || allFilled; i++) { std::string op = "script" + StrUtils::ToString(i+1); // script1..script10 std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString(i+1); // scriptReadOnly1..scriptReadOnly10 std::string opRunnable = "scriptRunnable" + StrUtils::ToString(i+1); // scriptRunnable1..scriptRunnable10 if (levelSource->GetParam(op)->IsDefined()) { std::string filename = levelSource->GetParam(op)->AsPath("ai"); GetLogger()->Trace("Loading program '%s' from level file\n", filename.c_str()); Program* program = AddProgram(); ReadProgram(program, filename); program->readOnly = levelSource->GetParam(opReadOnly)->AsBool(true); program->runnable = levelSource->GetParam(opRunnable)->AsBool(true); program->filename = levelSource->GetParam(op)->AsString(); if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) { dynamic_cast(*m_object).RunProgram(program); } } else { allFilled = false; } } if (loadSoluce && levelSource->GetParam("soluce")->IsDefined()) { std::string filename = levelSource->GetParam("soluce")->AsPath("ai"); GetLogger()->Trace("Loading program '%s' as soluce file\n", filename.c_str()); Program* program = AddProgram(); ReadProgram(program, filename); program->readOnly = true; program->runnable = false; program->filename = levelSource->GetParam("soluce")->AsString(); } if (m_programStorageIndex >= 0) { GetLogger()->Debug("Loading user programs from '%s%.3d___.txt'\n", userSource.c_str(), m_programStorageIndex); std::string dir = userSource.substr(0, userSource.find_last_of("/")); std::string file = userSource.substr(userSource.find_last_of("/")+1) + StrUtils::Format("%.3d([0-9]{3})\\.txt", m_programStorageIndex); std::regex regex(file); for (const std::string& filename : CResourceManager::ListFiles(dir)) { std::smatch matches; if (std::regex_match(filename, matches, regex)) { unsigned int i = std::stoul(matches[1]); Program* program = GetOrAddProgram(i); if(GetCompile(program)) program = AddProgram(); // If original slot is already used, get a new one GetLogger()->Trace("Loading program '%s/%s' from user directory\n", dir.c_str(), filename.c_str()); ReadProgram(program, dir+"/"+filename); } } } } void CProgramStorageObjectImpl::SaveAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) { levelSourceLine->AddParam("programStorageIndex", std::make_unique(m_programStorageIndex)); for (unsigned int i = 0; i < m_program.size(); i++) { if (!m_program[i]->filename.empty() && m_program[i]->readOnly) { levelSourceLine->AddParam("script" + StrUtils::ToString(i+1), std::make_unique(m_program[i]->filename)); levelSourceLine->AddParam("scriptReadOnly" + StrUtils::ToString(i+1), std::make_unique(m_program[i]->readOnly)); levelSourceLine->AddParam("scriptRunnable" + StrUtils::ToString(i+1), std::make_unique(m_program[i]->runnable)); } } if (m_programStorageIndex < 0) return; if (!m_object->Implements(ObjectInterfaceType::Controllable) || !dynamic_cast(*m_object).GetSelectable() || m_object->GetType() == OBJECT_HUMAN) return; GetLogger()->Debug("Saving saved scene programs to '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex); for (unsigned int i = 0; i < m_program.size(); i++) { std::string filename = levelSource + StrUtils::Format("/prog%.3d%.3d.txt", m_programStorageIndex, i); if (!m_program[i]->filename.empty() && m_program[i]->readOnly) continue; GetLogger()->Trace("Saving program '%s' to saved scene\n", filename.c_str()); WriteProgram(m_program[i].get(), filename); levelSourceLine->AddParam("scriptReadOnly" + StrUtils::ToString(i+1), std::make_unique(m_program[i]->readOnly)); levelSourceLine->AddParam("scriptRunnable" + StrUtils::ToString(i+1), std::make_unique(m_program[i]->runnable)); } std::regex regex(StrUtils::Format("prog%.3d([0-9]{3})\\.txt", m_programStorageIndex)); for (const std::string& filename : CResourceManager::ListFiles(levelSource)) { std::smatch matches; if (std::regex_match(filename, matches, regex)) { unsigned int id = std::stoul(matches[1]); if (id >= m_program.size() || !m_program[id]->filename.empty()) { GetLogger()->Trace("Removing old program '%s/%s' from saved scene\n", levelSource.c_str(), filename.c_str()); CResourceManager::Remove(levelSource+"/"+filename); } } } } void CProgramStorageObjectImpl::LoadAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) { int run = levelSourceLine->GetParam("run")->AsInt(0)-1; m_programStorageIndex = levelSourceLine->GetParam("programStorageIndex")->AsInt(-1); for (int i = 0; i <= 999; i++) { std::string op = "script" + StrUtils::ToString(i+1); // script1..script10 std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString(i+1); // scriptReadOnly1..scriptReadOnly10 std::string opRunnable = "scriptRunnable" + StrUtils::ToString(i+1); // scriptRunnable1..scriptRunnable10 if (levelSourceLine->GetParam(op)->IsDefined()) { std::string filename = levelSourceLine->GetParam(op)->AsPath("ai"); GetLogger()->Trace("Loading program '%s' from saved scene\n", filename.c_str()); Program* program = GetOrAddProgram(i); ReadProgram(program, filename); program->readOnly = levelSourceLine->GetParam(opReadOnly)->AsBool(true); program->runnable = levelSourceLine->GetParam(opRunnable)->AsBool(true); program->filename = levelSourceLine->GetParam(op)->AsString(); if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) { dynamic_cast(*m_object).RunProgram(program); } } } if(m_programStorageIndex < 0) return; GetLogger()->Debug("Loading saved scene programs from '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex); for (int i = 0; i <= 999; i++) { std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString(i+1); // scriptReadOnly1..scriptReadOnly10 std::string opRunnable = "scriptRunnable" + StrUtils::ToString(i+1); // scriptRunnable1..scriptRunnable10 std::string filename = levelSource + StrUtils::Format("/prog%.3d%.3d.txt", m_programStorageIndex, i); if (CResourceManager::Exists(filename)) { GetLogger()->Trace("Loading program '%s' from saved scene\n", filename.c_str()); Program* program = GetOrAddProgram(i); ReadProgram(program, filename); program->readOnly = levelSourceLine->GetParam(opReadOnly)->AsBool(true); program->runnable = levelSourceLine->GetParam(opRunnable)->AsBool(true); if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) { dynamic_cast(*m_object).RunProgram(program); } } } // Disable automatic user program storage now!! // This is to prevent overwriting auto-saved user programs with older versions from saved scenes m_allowProgramSave = false; }