1 #include "computer.h"
2 
3 #include <clocale>
4 #include <cstdlib>
5 #include <sstream>
6 
7 #include "debug.h"
8 #include "enum_conversions.h"
9 #include "json.h"
10 #include "output.h"
11 #include "translations.h"
12 
13 template <typename E> struct enum_traits;
14 
computer_option()15 computer_option::computer_option()
16     : name( "Unknown" ), action( COMPACT_NULL ), security( 0 )
17 {
18 }
19 
computer_option(const std::string & N,computer_action A,int S)20 computer_option::computer_option( const std::string &N, computer_action A, int S )
21     : name( N ), action( A ), security( S )
22 {
23 }
24 
serialize(JsonOut & jout) const25 void computer_option::serialize( JsonOut &jout ) const
26 {
27     jout.start_object();
28     jout.member( "name", name );
29     jout.member( "action" );
30     jout.write_as_string( action );
31     jout.member( "security", security );
32     jout.end_object();
33 }
34 
deserialize(JsonIn & jin)35 void computer_option::deserialize( JsonIn &jin )
36 {
37     const JsonObject jo = jin.get_object();
38     name = jo.get_string( "name" );
39     action = jo.get_enum_value<computer_action>( "action" );
40     security = jo.get_int( "security" );
41 }
42 
computer_failure()43 computer_failure::computer_failure()
44     : type( COMPFAIL_NULL )
45 {
46 }
47 
serialize(JsonOut & jout) const48 void computer_failure::serialize( JsonOut &jout ) const
49 {
50     jout.start_object();
51     jout.member( "action" );
52     jout.write_as_string( type );
53     jout.end_object();
54 }
55 
deserialize(JsonIn & jin)56 void computer_failure::deserialize( JsonIn &jin )
57 {
58     const JsonObject jo = jin.get_object();
59     type = jo.get_enum_value<computer_failure_type>( "action" );
60 }
61 
computer(const std::string & new_name,int new_security)62 computer::computer( const std::string &new_name, int new_security )
63     : name( new_name ), mission_id( -1 ), security( new_security ), alerts( 0 ),
64       next_attempt( calendar::before_time_starts ),
65       access_denied( _( "ERROR!  Access denied!" ) )
66 {
67 }
68 
set_security(int Security)69 void computer::set_security( int Security )
70 {
71     security = Security;
72 }
73 
add_option(const computer_option & opt)74 void computer::add_option( const computer_option &opt )
75 {
76     options.emplace_back( opt );
77 }
78 
add_option(const std::string & opt_name,computer_action action,int security)79 void computer::add_option( const std::string &opt_name, computer_action action,
80                            int security )
81 {
82     add_option( computer_option( opt_name, action, security ) );
83 }
84 
add_failure(const computer_failure & failure)85 void computer::add_failure( const computer_failure &failure )
86 {
87     failures.emplace_back( failure );
88 }
89 
add_failure(computer_failure_type failure)90 void computer::add_failure( computer_failure_type failure )
91 {
92     add_failure( computer_failure( failure ) );
93 }
94 
set_access_denied_msg(const std::string & new_msg)95 void computer::set_access_denied_msg( const std::string &new_msg )
96 {
97     access_denied = new_msg;
98 }
99 
set_mission(const int id)100 void computer::set_mission( const int id )
101 {
102     mission_id = id;
103 }
104 
105 static computer_action computer_action_from_legacy_enum( int val );
106 static computer_failure_type computer_failure_type_from_legacy_enum( int val );
107 
load_legacy_data(const std::string & data)108 void computer::load_legacy_data( const std::string &data )
109 {
110     options.clear();
111     failures.clear();
112 
113     std::istringstream dump( data );
114     dump.imbue( std::locale::classic() );
115 
116     dump >> name >> security >> mission_id;
117 
118     name = string_replace( name, "_", " " );
119 
120     // Pull in options
121     int optsize;
122     dump >> optsize;
123     for( int n = 0; n < optsize; n++ ) {
124         std::string tmpname;
125 
126         int tmpaction;
127         int tmpsec;
128 
129         dump >> tmpname >> tmpaction >> tmpsec;
130         // Legacy missle launch option that got removed before `computer_action` was
131         // refactored to be saved and loaded as string ids. Do not change this number:
132         // `computer_action` now has different underlying values from back then!
133         if( tmpaction == 15 ) {
134             continue;
135         }
136         add_option( string_replace( tmpname, "_", " " ), computer_action_from_legacy_enum( tmpaction ),
137                     tmpsec );
138     }
139 
140     // Pull in failures
141     int failsize;
142     dump >> failsize;
143     for( int n = 0; n < failsize; n++ ) {
144         int tmpfail;
145         dump >> tmpfail;
146         add_failure( computer_failure_type_from_legacy_enum( tmpfail ) );
147     }
148 
149     std::string tmp_access_denied;
150     dump >> tmp_access_denied;
151 
152     // For backwards compatibility, only set the access denied message if it
153     // isn't empty. This is to avoid the message becoming blank when people
154     // load old saves.
155     if( !tmp_access_denied.empty() ) {
156         access_denied = string_replace( tmp_access_denied, "_", " " );
157     }
158 }
159 
serialize(JsonOut & jout) const160 void computer::serialize( JsonOut &jout ) const
161 {
162     jout.start_object();
163     jout.member( "name", name );
164     jout.member( "mission", mission_id );
165     jout.member( "security", security );
166     jout.member( "alerts", alerts );
167     jout.member( "next_attempt", next_attempt );
168     jout.member( "options", options );
169     jout.member( "failures", failures );
170     jout.member( "access_denied", access_denied );
171     jout.end_object();
172 }
173 
deserialize(JsonIn & jin)174 void computer::deserialize( JsonIn &jin )
175 {
176     if( jin.test_string() ) {
177         load_legacy_data( jin.get_string() );
178     } else {
179         const JsonObject jo = jin.get_object();
180         jo.read( "name", name );
181         jo.read( "mission", mission_id );
182         jo.read( "security", security );
183         jo.read( "alerts", alerts );
184         jo.read( "next_attempt", next_attempt );
185         jo.read( "options", options );
186         jo.read( "failures", failures );
187         jo.read( "access_denied", access_denied );
188     }
189 }
190 
remove_option(computer_action const action)191 void computer::remove_option( computer_action const action )
192 {
193     for( auto it = options.begin(); it != options.end(); ++it ) {
194         if( it->action == action ) {
195             options.erase( it );
196             break;
197         }
198     }
199 }
200 
computer_action_from_legacy_enum(const int val)201 static computer_action computer_action_from_legacy_enum( const int val )
202 {
203     switch( val ) {
204         // Used to migrate old saves. Do not change the numbers!
205         // *INDENT-OFF*
206         default: return COMPACT_NULL;
207         case 0: return COMPACT_NULL;
208         case 1: return COMPACT_OPEN;
209         case 2: return COMPACT_LOCK;
210         case 3: return COMPACT_UNLOCK;
211         case 4: return COMPACT_TOLL;
212         case 5: return COMPACT_SAMPLE;
213         case 6: return COMPACT_RELEASE;
214         case 7: return COMPACT_RELEASE_BIONICS;
215         case 8: return COMPACT_TERMINATE;
216         case 9: return COMPACT_PORTAL;
217         case 10: return COMPACT_CASCADE;
218         case 11: return COMPACT_RESEARCH;
219         case 12: return COMPACT_MAPS;
220         case 13: return COMPACT_MAP_SEWER;
221         case 14: return COMPACT_MAP_SUBWAY;
222         // options with action enum 15 are removed in load_legacy_data()
223         case 16: return COMPACT_MISS_DISARM;
224         case 17: return COMPACT_LIST_BIONICS;
225         case 18: return COMPACT_ELEVATOR_ON;
226         case 19: return COMPACT_AMIGARA_LOG;
227         case 20: return COMPACT_AMIGARA_START;
228         case 21: return COMPACT_COMPLETE_DISABLE_EXTERNAL_POWER;
229         case 22: return COMPACT_REPEATER_MOD;
230         case 23: return COMPACT_DOWNLOAD_SOFTWARE;
231         case 24: return COMPACT_BLOOD_ANAL;
232         case 25: return COMPACT_DATA_ANAL;
233         case 26: return COMPACT_DISCONNECT;
234         case 27: return COMPACT_EMERG_MESS;
235         case 28: return COMPACT_EMERG_REF_CENTER;
236         case 29: return COMPACT_TOWER_UNRESPONSIVE;
237         case 30: return COMPACT_SR1_MESS;
238         case 31: return COMPACT_SR2_MESS;
239         case 32: return COMPACT_SR3_MESS;
240         case 33: return COMPACT_SR4_MESS;
241         case 34: return COMPACT_SRCF_1_MESS;
242         case 35: return COMPACT_SRCF_2_MESS;
243         case 36: return COMPACT_SRCF_3_MESS;
244         case 37: return COMPACT_SRCF_SEAL_ORDER;
245         case 38: return COMPACT_SRCF_SEAL;
246         case 39: return COMPACT_SRCF_ELEVATOR;
247         case 40: return COMPACT_OPEN_DISARM;
248         case 41: return COMPACT_UNLOCK_DISARM;
249         case 42: return COMPACT_RELEASE_DISARM;
250         case 43: return COMPACT_IRRADIATOR;
251         case 44: return COMPACT_GEIGER;
252         case 45: return COMPACT_CONVEYOR;
253         case 46: return COMPACT_SHUTTERS;
254         case 47: return COMPACT_EXTRACT_RAD_SOURCE;
255         case 48: return COMPACT_DEACTIVATE_SHOCK_VENT;
256         case 49: return COMPACT_RADIO_ARCHIVE;
257         // *INDENT-ON*
258     }
259 }
260 
computer_failure_type_from_legacy_enum(const int val)261 static computer_failure_type computer_failure_type_from_legacy_enum( const int val )
262 {
263     switch( val ) {
264         // Used to migrate old saves. Do not change the numbers!
265         // *INDENT-OFF*
266         default: return COMPFAIL_NULL;
267         case 0: return COMPFAIL_NULL;
268         case 1: return COMPFAIL_SHUTDOWN;
269         case 2: return COMPFAIL_ALARM;
270         case 3: return COMPFAIL_MANHACKS;
271         case 4: return COMPFAIL_SECUBOTS;
272         case 5: return COMPFAIL_DAMAGE;
273         case 6: return COMPFAIL_PUMP_EXPLODE;
274         case 7: return COMPFAIL_PUMP_LEAK;
275         case 8: return COMPFAIL_AMIGARA;
276         case 9: return COMPFAIL_DESTROY_BLOOD;
277         case 10: return COMPFAIL_DESTROY_DATA;
278         // *INDENT-ON*
279     }
280 }
281 
282 namespace io
283 {
284 template<>
enum_to_string(const computer_action act)285 std::string enum_to_string<computer_action>( const computer_action act )
286 {
287     switch( act ) {
288         // *INDENT-OFF*
289         case COMPACT_NULL: return "null";
290         case COMPACT_AMIGARA_LOG: return "amigara_log";
291         case COMPACT_AMIGARA_START: return "amigara_start";
292         case COMPACT_BLOOD_ANAL: return "blood_anal";
293         case COMPACT_CASCADE: return "cascade";
294         case COMPACT_COMPLETE_DISABLE_EXTERNAL_POWER: return "complete_disable_external_power";
295         case COMPACT_CONVEYOR: return "conveyor";
296         case COMPACT_DATA_ANAL: return "data_anal";
297         case COMPACT_DEACTIVATE_SHOCK_VENT: return "deactivate_shock_vent";
298         case COMPACT_DISCONNECT: return "disconnect";
299         case COMPACT_DOWNLOAD_SOFTWARE: return "download_software";
300         case COMPACT_ELEVATOR_ON: return "elevator_on";
301         case COMPACT_EMERG_MESS: return "emerg_mess";
302         case COMPACT_EMERG_REF_CENTER: return "emerg_ref_center";
303         case COMPACT_EXTRACT_RAD_SOURCE: return "extract_rad_source";
304         case COMPACT_GEIGER: return "geiger";
305         case COMPACT_IRRADIATOR: return "irradiator";
306         case COMPACT_LIST_BIONICS: return "list_bionics";
307         case COMPACT_LOCK: return "lock";
308         case COMPACT_MAP_SEWER: return "map_sewer";
309         case COMPACT_MAP_SUBWAY: return "map_subway";
310         case COMPACT_MAPS: return "maps";
311         case COMPACT_MISS_DISARM: return "miss_disarm";
312         case COMPACT_OPEN: return "open";
313         case COMPACT_OPEN_DISARM: return "open_disarm";
314         case COMPACT_PORTAL: return "portal";
315         case COMPACT_RADIO_ARCHIVE: return "radio_archive";
316         case COMPACT_RELEASE: return "release";
317         case COMPACT_RELEASE_BIONICS: return "release_bionics";
318         case COMPACT_RELEASE_DISARM: return "release_disarm";
319         case COMPACT_REPEATER_MOD: return "repeater_mod";
320         case COMPACT_RESEARCH: return "research";
321         case COMPACT_SAMPLE: return "sample";
322         case COMPACT_SHUTTERS: return "shutters";
323         case COMPACT_SR1_MESS: return "sr1_mess";
324         case COMPACT_SR2_MESS: return "sr2_mess";
325         case COMPACT_SR3_MESS: return "sr3_mess";
326         case COMPACT_SR4_MESS: return "sr4_mess";
327         case COMPACT_SRCF_1_MESS: return "srcf_1_mess";
328         case COMPACT_SRCF_2_MESS: return "srcf_2_mess";
329         case COMPACT_SRCF_3_MESS: return "srcf_3_mess";
330         case COMPACT_SRCF_ELEVATOR: return "srcf_elevator";
331         case COMPACT_SRCF_SEAL: return "srcf_seal";
332         case COMPACT_SRCF_SEAL_ORDER: return "srcf_seal_order";
333         case COMPACT_TERMINATE: return "terminate";
334         case COMPACT_TOLL: return "toll";
335         case COMPACT_TOWER_UNRESPONSIVE: return "tower_unresponsive";
336         case COMPACT_UNLOCK: return "unlock";
337         case COMPACT_UNLOCK_DISARM: return "unlock_disarm";
338         // *INDENT-OFF*
339         case NUM_COMPUTER_ACTIONS:
340             break;
341     }
342     debugmsg( "Invalid computer_action" );
343     abort();
344 }
345 
346 template<>
enum_to_string(const computer_failure_type fail)347 std::string enum_to_string<computer_failure_type>( const computer_failure_type fail )
348 {
349     switch( fail ){
350         // *INDENT-OFF*
351         case COMPFAIL_NULL: return "null";
352         case COMPFAIL_ALARM: return "alarm";
353         case COMPFAIL_AMIGARA: return "amigara";
354         case COMPFAIL_DAMAGE: return "damage";
355         case COMPFAIL_DESTROY_BLOOD: return "destroy_blood";
356         case COMPFAIL_DESTROY_DATA: return "destroy_data";
357         case COMPFAIL_MANHACKS: return "manhacks";
358         case COMPFAIL_PUMP_EXPLODE: return "pump_explode";
359         case COMPFAIL_PUMP_LEAK: return "pump_leak";
360         case COMPFAIL_SECUBOTS: return "secubots";
361         case COMPFAIL_SHUTDOWN: return "shutdown";
362         // *INDENT-ON*
363         case NUM_COMPUTER_FAILURES:
364             break;
365     }
366     debugmsg( "Invalid computer_failure_type" );
367     abort();
368 }
369 } // namespace io
370 
371 template<>
372 struct enum_traits<computer_action> {
373     static constexpr computer_action last = NUM_COMPUTER_ACTIONS;
374 };
375 
376 template<>
377 struct enum_traits<computer_failure_type> {
378     static constexpr computer_failure_type last = NUM_COMPUTER_FAILURES;
379 };
380 
from_json(const JsonObject & jo)381 computer_option computer_option::from_json( const JsonObject &jo )
382 {
383     translation name;
384     jo.read( "name", name );
385     const computer_action action = jo.get_enum_value<computer_action>( "action" );
386     const int sec = jo.get_int( "security", 0 );
387     return computer_option( name.translated(), action, sec );
388 }
389 
from_json(const JsonObject & jo)390 computer_failure computer_failure::from_json( const JsonObject &jo )
391 {
392     const computer_failure_type type = jo.get_enum_value<computer_failure_type>( "action" );
393     return computer_failure( type );
394 }
395