|
2 | 2 |
|
3 | 3 | #include "mission/object.h" |
4 | 4 |
|
| 5 | +// Sub-texture type suffixes, mirroring the strcat_s calls in modelread.cpp. |
| 6 | +// Used both when detecting sub-texture slots in initSubTypes and when parsing |
| 7 | +// new_texture strings on dialog reload. |
| 8 | +static const SCP_string SUBTEXTURE_SUFFIXES[] = { "misc", "shine", "glow", "normal", "height", "ao", "reflect" }; |
| 9 | + |
5 | 10 | namespace fso { |
6 | 11 | namespace fred { |
7 | 12 | namespace dialogs { |
@@ -76,24 +81,55 @@ namespace fso { |
76 | 81 | { |
77 | 82 | if (!stricmp(Ships[_editor->cur_ship].ship_name, Fred_texture_replacement.ship_name) && !(Fred_texture_replacement.from_table)) |
78 | 83 | { |
| 84 | + // old_texture is stored as the bare base name by this dialog (no type suffix). |
| 85 | + // However, entries loaded from old mission files may have a type suffix |
| 86 | + // (e.g. "fenris-body-misc"), so fall back to stripping if no direct match. |
79 | 87 | SCP_string pureName = Fred_texture_replacement.old_texture; |
80 | | - auto npos = pureName.find_last_of('-'); |
81 | | - if (npos != SCP_string::npos) { |
82 | | - pureName = pureName.substr(0, pureName.find_last_of('-')); |
| 88 | + |
| 89 | + // Find the matching default texture slot. |
| 90 | + // Try direct match first; fall back to stripping the last '-' segment |
| 91 | + // for old mission-file entries that stored old_texture with a type suffix. |
| 92 | + size_t matchIdx = defaultTextures.size(); |
| 93 | + for (size_t i = 0; i < defaultTextures.size(); i++) { |
| 94 | + if (lcase_equal(defaultTextures[i], pureName)) { |
| 95 | + matchIdx = i; |
| 96 | + break; |
| 97 | + } |
| 98 | + } |
| 99 | + if (matchIdx == defaultTextures.size()) { |
| 100 | + auto stripPos = pureName.find_last_of('-'); |
| 101 | + if (stripPos != SCP_string::npos) { |
| 102 | + SCP_string stripped = pureName.substr(0, stripPos); |
| 103 | + for (size_t i = 0; i < defaultTextures.size(); i++) { |
| 104 | + if (lcase_equal(defaultTextures[i], stripped)) { |
| 105 | + matchIdx = i; |
| 106 | + break; |
| 107 | + } |
| 108 | + } |
| 109 | + } |
83 | 110 | } |
84 | 111 |
|
85 | | - // look for corresponding old texture |
86 | | - for (size_t i = 0; i < defaultTextures.size(); i++) |
| 112 | + if (matchIdx < defaultTextures.size()) |
87 | 113 | { |
88 | | - // if match |
89 | | - if (lcase_equal(defaultTextures[i], pureName)) |
| 114 | + size_t i = matchIdx; |
90 | 115 | { |
91 | 116 | SCP_string newText = Fred_texture_replacement.new_texture; |
92 | | - npos = newText.find_last_of('-'); |
93 | 117 | SCP_string type; |
94 | | - if (npos != SCP_string::npos) { |
95 | | - type = newText.substr(npos + 1); |
96 | | - newText = newText.substr(0, newText.find_last_of('-')); |
| 118 | + { |
| 119 | + auto npos = newText.find_last_of('-'); |
| 120 | + if (npos != SCP_string::npos) { |
| 121 | + SCP_string possibleType = newText.substr(npos + 1); |
| 122 | + // Only treat the suffix as a type if it's a known sub-texture type. |
| 123 | + // Texture names themselves can contain hyphens (e.g. "fighter01-01a"), |
| 124 | + // so we must not blindly strip the last segment. |
| 125 | + for (const auto& kt : SUBTEXTURE_SUFFIXES) { |
| 126 | + if (lcase_equal(possibleType, kt)) { |
| 127 | + type = possibleType; |
| 128 | + newText = newText.substr(0, npos); |
| 129 | + break; |
| 130 | + } |
| 131 | + } |
| 132 | + } |
97 | 133 | } |
98 | 134 | if (!type.empty()) { |
99 | 135 | if (type == "misc") { |
@@ -142,8 +178,6 @@ namespace fso { |
142 | 178 | currentTextures[i]["main"] = newText; |
143 | 179 | } |
144 | 180 |
|
145 | | - // we found one, so no more to check |
146 | | - break; |
147 | 181 | } |
148 | 182 | } |
149 | 183 | } |
@@ -206,30 +240,19 @@ namespace fso { |
206 | 240 | } |
207 | 241 | if (!type.empty()) { |
208 | 242 | if (type == "trans") { |
209 | | - } |
210 | | - else if (type == "misc") { |
211 | | - subTypesAvailable[MapNum]["misc"] = true; |
212 | | - } |
213 | | - else if (type == "shine") { |
214 | | - subTypesAvailable[MapNum]["shine"] = true; |
215 | | - } |
216 | | - else if (type == "glow") { |
217 | | - subTypesAvailable[MapNum]["glow"] = true; |
218 | | - } |
219 | | - else if (type == "normal") { |
220 | | - subTypesAvailable[MapNum]["normal"] = true; |
221 | | - } |
222 | | - else if (type == "height") { |
223 | | - subTypesAvailable[MapNum]["height"] = true; |
224 | | - } |
225 | | - else if (type == "ao") { |
226 | | - subTypesAvailable[MapNum]["ao"] = true; |
227 | | - } |
228 | | - else if (type == "reflect") { |
229 | | - subTypesAvailable[MapNum]["reflect"] = true; |
230 | | - } |
231 | | - else { |
232 | | - error_display(1, "Invalid Map type %s. Check your model's texture names or get a programmer", type.c_str()); |
| 243 | + // transparency map, not a replaceable subtype |
| 244 | + } else { |
| 245 | + bool known = false; |
| 246 | + for (const auto& kt : SUBTEXTURE_SUFFIXES) { |
| 247 | + if (lcase_equal(type, kt)) { |
| 248 | + subTypesAvailable[MapNum][kt] = true; |
| 249 | + known = true; |
| 250 | + break; |
| 251 | + } |
| 252 | + } |
| 253 | + if (!known) { |
| 254 | + error_display(1, "Invalid Map type %s. Check your model's texture names or get a programmer", type.c_str()); |
| 255 | + } |
233 | 256 | } |
234 | 257 | } |
235 | 258 | } |
@@ -564,7 +587,7 @@ namespace fso { |
564 | 587 | { |
565 | 588 | temp_bmp = bm_load_animation(fullName.c_str(), &temp_frames, &temp_fps, nullptr, nullptr, false, true); |
566 | 589 | } |
567 | | - return temp_bmp < 0; |
| 590 | + return temp_bmp >= 0; |
568 | 591 | } |
569 | 592 | } |
570 | 593 |
|
|
0 commit comments