Fix race between killing object and drawing object
[lsnes.git] / src / core / emustatus.cpp
blob6c6650084e09af045d9f7a20da759a3e35a7945b
1 #include "cmdhelp/loadsave.hpp"
2 #include "core/advdumper.hpp"
3 #include "core/audioapi.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/emustatus.hpp"
6 #include "core/framerate.hpp"
7 #include "core/inthread.hpp"
8 #include "core/jukebox.hpp"
9 #include "core/memorywatch.hpp"
10 #include "core/movie.hpp"
11 #include "core/moviedata.hpp"
12 #include "core/moviefile.hpp"
13 #include "core/multitrack.hpp"
14 #include "core/project.hpp"
15 #include "core/rom.hpp"
16 #include "core/runmode.hpp"
17 #include "lua/lua.hpp"
19 #include <sstream>
21 const int _lsnes_status::pause_none = 0;
22 const int _lsnes_status::pause_normal = 1;
23 const int _lsnes_status::pause_break = 2;
24 const uint64_t _lsnes_status::subframe_savepoint = 0xFFFFFFFFFFFFFFFEULL;
25 const uint64_t _lsnes_status::subframe_video = 0xFFFFFFFFFFFFFFFFULL;
27 slotinfo_cache::slotinfo_cache(movie_logic& _mlogic, command::group& _cmd)
28 : mlogic(_mlogic), cmd(_cmd),
29 flushcmd(cmd, CLOADSAVE::flushslots, [this]() { this->flush(); })
33 std::string slotinfo_cache::get(const std::string& _filename)
35 std::string filename = resolve_relative_path(_filename);
36 if(!cache.count(filename)) {
37 std::ostringstream out;
38 try {
39 moviefile::brief_info info(filename);
40 if(!mlogic)
41 out << "No movie";
42 else if(mlogic.get_mfile().projectid == info.projectid)
43 out << info.rerecords << "R/" << info.current_frame << "F";
44 else
45 out << "Wrong movie";
46 } catch(...) {
47 out << "Nonexistent";
49 cache[filename] = out.str();
51 return cache[filename];
54 void slotinfo_cache::flush(const std::string& _filename)
56 cache.erase(resolve_relative_path(_filename));
59 void slotinfo_cache::flush()
61 cache.clear();
64 status_updater::status_updater(project_state& _project, movie_logic& _mlogic, voice_commentary& _commentary,
65 triplebuffer::triplebuffer<_lsnes_status>& _status, emulator_runmode& _runmode, master_dumper& _mdumper,
66 save_jukebox& _jukebox, slotinfo_cache& _slotcache, framerate_regulator& _framerate,
67 controller_state& _controls, multitrack_edit& _mteditor, lua_state& _lua2, loaded_rom& _rom,
68 memwatch_set& _mwatch, emulator_dispatch& _dispatch)
69 : project(_project), mlogic(_mlogic), commentary(_commentary), status(_status), runmode(_runmode),
70 mdumper(_mdumper), jukebox(_jukebox), slotcache(_slotcache), framerate(_framerate), controls(_controls),
71 mteditor(_mteditor), lua2(_lua2), rom(_rom), mwatch(_mwatch), dispatch(_dispatch)
76 void status_updater::update()
78 auto p = project.get();
79 bool readonly = false;
81 uint64_t magic[4];
82 rom.region_fill_framerate_magic(magic);
83 if(mlogic)
84 commentary.frame_number(mlogic.get_movie().get_current_frame(),
85 1.0 * magic[1] / magic[0]);
86 else
87 commentary.frame_number(0, 60.0); //Default.
89 auto& _status = status.get_write();
90 try {
91 if(mlogic && !runmode.is_corrupt()) {
92 _status.movie_valid = true;
93 _status.curframe = mlogic.get_movie().get_current_frame();
94 _status.length = mlogic.get_movie().get_frame_count();
95 _status.lag = mlogic.get_movie().get_lag_frames();
96 if(runmode.get_point() == emulator_runmode::P_START)
97 _status.subframe = 0;
98 else if(runmode.get_point() == emulator_runmode::P_SAVE)
99 _status.subframe = _lsnes_status::subframe_savepoint;
100 else if(runmode.get_point() == emulator_runmode::P_VIDEO)
101 _status.subframe = _lsnes_status::subframe_video;
102 else
103 _status.subframe = mlogic.get_movie().next_poll_number();
104 } else {
105 _status.movie_valid = false;
106 _status.curframe = 0;
107 _status.length = 0;
108 _status.lag = 0;
109 _status.subframe = 0;
111 _status.dumping = (mdumper.get_dumper_count() > 0);
112 if(runmode.is_paused_break())
113 _status.pause = _lsnes_status::pause_break;
114 else if(runmode.is_paused_normal())
115 _status.pause = _lsnes_status::pause_normal;
116 else
117 _status.pause = _lsnes_status::pause_none;
118 if(mlogic) {
119 auto& mo = mlogic.get_movie();
120 readonly = mo.readonly_mode();
121 if(runmode.is_corrupt())
122 _status.mode = 'C';
123 else if(!readonly)
124 _status.mode = 'R';
125 else if(mo.get_frame_count() >= mo.get_current_frame())
126 _status.mode = 'P';
127 else
128 _status.mode = 'F';
130 try {
131 _status.saveslot_valid = true;
132 int tmp = -1;
133 std::string sfilen = translate_name_mprefix(jukebox.get_slot_name(), tmp, -1);
134 _status.saveslot = jukebox.get_slot() + 1;
135 _status.slotinfo = utf8::to32(slotcache.get(sfilen));
136 } catch(...) {
137 _status.saveslot_valid = false;
139 _status.branch_valid = (p != NULL);
140 if(p) _status.branch = utf8::to32(p->get_branch_string());
142 std::string cur_branch = mlogic ? mlogic.get_mfile().current_branch() :
144 _status.mbranch_valid = (cur_branch != "");
145 _status.mbranch = utf8::to32(cur_branch);
147 _status.speed = (unsigned)(100 * framerate.get_realized_multiplier() + 0.5);
149 if(mlogic && !runmode.is_corrupt()) {
150 time_t timevalue = static_cast<time_t>(mlogic.get_mfile().dyn.rtc_second);
151 struct tm* time_decompose = gmtime(&timevalue);
152 char datebuffer[512];
153 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
154 _status.rtc = utf8::to32(datebuffer);
155 _status.rtc_valid = true;
156 } else {
157 _status.rtc_valid = false;
160 auto mset = controls.active_macro_set();
161 bool mfirst = true;
162 std::ostringstream mss;
163 for(auto i: mset) {
164 if(!mfirst) mss << ",";
165 mss << i;
166 mfirst = false;
168 _status.macros = utf8::to32(mss.str());
170 portctrl::frame c;
171 if(!mteditor.any_records())
172 c = mlogic.get_movie().get_controls();
173 else
174 c = controls.get_committed();
175 _status.inputs.clear();
176 for(unsigned i = 0;; i++) {
177 auto pindex = controls.lcid_to_pcid(i);
178 if(pindex.first < 0 || !controls.is_present(pindex.first, pindex.second))
179 break;
180 char32_t buffer[MAX_DISPLAY_LENGTH];
181 c.display(pindex.first, pindex.second, buffer);
182 std::u32string _buffer = buffer;
183 if(readonly && mteditor.is_enabled()) {
184 multitrack_edit::state st = mteditor.get(pindex.first, pindex.second);
185 if(st == multitrack_edit::MT_PRESERVE)
186 _buffer += U" (keep)";
187 else if(st == multitrack_edit::MT_OVERWRITE)
188 _buffer += U" (rewrite)";
189 else if(st == multitrack_edit::MT_OR)
190 _buffer += U" (OR)";
191 else if(st == multitrack_edit::MT_XOR)
192 _buffer += U" (XOR)";
193 else
194 _buffer += U" (\?\?\?)";
196 _status.inputs.push_back(_buffer);
198 //Lua variables.
199 _status.lvars = lua2.get_watch_vars();
200 //Memory watches.
201 _status.mvars = mwatch.get_window_vars();
203 _status.valid = true;
204 } catch(...) {
206 status.put_write();
207 dispatch.status_update();