mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 22:54:54 +01:00
Found with an address and undefined behaviour sanitizer build, random monkey sessions on the test sceneries and broken inputs: - A deleted model left its pointer in the pick list of the renderer, the bound node of the panels, the bindings of the infrastructure and the selection: the next pick or panel frame read freed memory. The renderer forgets it, and one erase_model does the cleaning for all the places which deleted a model by hand. - The plan band of a route read the vertex before the first one in the frame a drag was let go. - A scenery which can't be opened goes back to the launcher with the reason in the log, a track of an unknown type or a tributary loads as a normal track or a river with a message, and a spline with no finite length or step gets a sane number of segments. - Typed numbers which aren't (NaN, inf, ends 0 m or 1000 km apart, a vector of 1e30, a diverging track part of 5 km, a curve threshold of 0, a zero history size, radius norms below zero) are refused or clamped, where they made the geometry run away or divide by zero. - Folders which can't be read and file names which aren't valid in the locale no longer throw out of the scans of sceneries, textures, sweeps, roads and signal plates; the scans skip them. - The launcher: Ctrl+Del with no trainset selected, a dropped vehicle whose skin is gone and a text with a percent sign. - A terrain mesh made for another resolution of its chunk isn't put into the chunk, and a slot is reused only for a mesh of the same size. - Vehicles on a track changed by the editor are moved over a copy of the list they leave. - The table of the keyboard shortcuts gives its stretched column a weight, else ImGui divides 0 by 0 in the first frame of the window.
184 lines
4.8 KiB
C++
184 lines
4.8 KiB
C++
#include "stdafx.h"
|
|
#include "scenery_scanner.h"
|
|
#include "utilities/Logs.h"
|
|
|
|
scenery_scanner::scenery_scanner(ui::vehicles_bank &bank)
|
|
: bank(bank)
|
|
{
|
|
|
|
}
|
|
|
|
void scenery_scanner::scan()
|
|
{
|
|
std::error_code error;
|
|
for (std::filesystem::directory_iterator f{"scenery", std::filesystem::directory_options::skip_permission_denied, error}, end; f != end; f.increment(error)) {
|
|
std::filesystem::path path(std::filesystem::relative(f->path(), "scenery/", error));
|
|
std::string name;
|
|
try {
|
|
name = path.string();
|
|
}
|
|
catch (std::exception const &) {
|
|
continue;
|
|
}
|
|
|
|
if (name.empty() || path.filename().string().front() == '$')
|
|
continue;
|
|
|
|
if (name.ends_with(".scn"))
|
|
scan_scn(path);
|
|
}
|
|
|
|
std::sort(scenarios.begin(), scenarios.end(),
|
|
[](const scenery_desc &a, const scenery_desc &b) { return a.path < b.path; });
|
|
|
|
build_categories();
|
|
}
|
|
|
|
void scenery_scanner::scan_scn(std::filesystem::path path)
|
|
{
|
|
scenarios.emplace_back();
|
|
scenery_desc &desc = scenarios.back();
|
|
desc.path = path;
|
|
|
|
std::string file_path = "scenery/" + path.string();
|
|
|
|
cParser parser(file_path, cParser::buffer_FILE);
|
|
parser.expandIncludes = false;
|
|
while (!parser.eof()) {
|
|
parser.getTokens();
|
|
if (parser.peek() == "trainset")
|
|
parse_trainset(parser);
|
|
}
|
|
|
|
std::ifstream stream(file_path, std::ios_base::binary | std::ios_base::in);
|
|
|
|
int line_counter = 0;
|
|
std::string line;
|
|
while (std::getline(stream, line))
|
|
{
|
|
line_counter++;
|
|
|
|
if (line.size() < 6 || !line.starts_with("//$"))
|
|
continue;
|
|
|
|
if (line[3] == 'i')
|
|
desc.image_path = "scenery/images/" + line.substr(5);
|
|
else if (line[3] == 'n')
|
|
desc.name = win1250_to_utf8(line.substr(5));
|
|
else if (line[3] == 'd')
|
|
desc.description += win1250_to_utf8(line.substr(5)) + '\n';
|
|
else if (line[3] == 'l')
|
|
desc.category = win1250_to_utf8(line.substr(5));
|
|
else if (line[3] == 'f') {
|
|
std::string lang;
|
|
std::string file;
|
|
std::string label;
|
|
|
|
std::istringstream stream(line.substr(5));
|
|
stream >> lang >> file;
|
|
std::getline(stream, label);
|
|
|
|
desc.links.push_back(std::make_pair(file, win1250_to_utf8(label)));
|
|
}
|
|
else if (line[3] == 'o') {
|
|
for (auto &trainset : desc.trainsets) {
|
|
if (line_counter < trainset.file_bounds.first
|
|
|| line_counter > trainset.file_bounds.second)
|
|
continue;
|
|
|
|
trainset.description = win1250_to_utf8(line.substr(5));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void scenery_scanner::parse_trainset(cParser &parser)
|
|
{
|
|
scenery_desc &desc = scenarios.back();
|
|
desc.trainsets.emplace_back();
|
|
auto &trainset = desc.trainsets.back();
|
|
|
|
trainset.file_bounds.first = parser.Line();
|
|
parser.getTokens(4);
|
|
parser >> trainset.name >> trainset.track >> trainset.offset >> trainset.velocity;
|
|
|
|
parser.getTokens();
|
|
if (parser.peek() == "reversed") {
|
|
trainset.reversed = true;
|
|
parser.getTokens();
|
|
}
|
|
while (parser.peek() == "node") {
|
|
trainset.vehicles.emplace_back();
|
|
dynamic_desc &dyn = trainset.vehicles.back();
|
|
|
|
std::string datafolder, skinfile, mmdfile, params;
|
|
|
|
parser.getTokens(2); // range_max, range_min
|
|
parser.getTokens(1, false); // name
|
|
parser >> dyn.name;
|
|
|
|
if (!parser.expectToken("dynamic"))
|
|
break;
|
|
|
|
parser.getTokens(7, false);
|
|
parser >> datafolder >> skinfile >> mmdfile >> dyn.offset >> dyn.drivertype >> params >> dyn.loadcount;
|
|
|
|
size_t params_pos = params.find('.');
|
|
if (params_pos != -1 && params_pos < params.size()) {
|
|
dyn.params = params.substr(params_pos + 1, -1);
|
|
params.erase(params_pos);
|
|
}
|
|
|
|
{
|
|
std::istringstream couplingparser(params);
|
|
couplingparser >> dyn.coupling;
|
|
}
|
|
|
|
skinfile = ToLower(skinfile);
|
|
erase_extension(skinfile);
|
|
replace_slashes(datafolder);
|
|
auto vehicle_path = std::filesystem::path(ToLower(datafolder)) / ToLower(mmdfile);
|
|
|
|
auto it = bank.vehicles.find(vehicle_path);
|
|
if (it != bank.vehicles.end()) {
|
|
dyn.vehicle = it->second;
|
|
bool found = false;
|
|
|
|
for (const std::shared_ptr<ui::skin_set> &vehicle_skin : it->second->matching_skinsets) {
|
|
if (vehicle_skin->skin != skinfile)
|
|
continue;
|
|
|
|
dyn.skin = vehicle_skin;
|
|
found = true;
|
|
break;
|
|
}
|
|
if (!found && skinfile != "none")
|
|
ErrorLog("skin not found: " + skinfile + ", vehicle type: " + vehicle_path.string() + ", file: " + parser.Name(), logtype::file);
|
|
}
|
|
else {
|
|
ErrorLog("vehicle type not found: " + vehicle_path.string() + ", file: " + parser.Name(), logtype::file);
|
|
}
|
|
|
|
parser.getTokens();
|
|
if (parser.peek() != "enddynamic") {
|
|
parser >> dyn.loadtype;
|
|
parser.getTokens();
|
|
}
|
|
|
|
parser.getTokens();
|
|
}
|
|
|
|
trainset.file_bounds.second = parser.Line();
|
|
}
|
|
|
|
void scenery_scanner::build_categories()
|
|
{
|
|
for (auto &desc : scenarios) {
|
|
std::string name = desc.path.stem().string();
|
|
std::string prefix = desc.category;
|
|
// If the scenario does have a category, add it to the list so we can render it later in a group.
|
|
if (!prefix.empty())
|
|
categories[prefix].push_back(&desc);
|
|
}
|
|
}
|