diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index 282cc5c256c..501ba5d783b 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -2454,6 +2454,9 @@ ioport_manager::ioport_manager(running_machine &machine) m_playback_file(machine.options().input_directory(), OPEN_FLAG_READ), m_playback_accumulated_speed(0), m_playback_accumulated_frames(0), + m_timecode_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS), + m_timecode_count(0), + m_timecode_last_time(attotime::zero), m_has_configs(false), m_has_analog(false), m_has_dips(false), @@ -2564,6 +2567,7 @@ time_t ioport_manager::initialize() // open playback and record files if specified time_t basetime = playback_init(); record_init(); + timecode_init(); return basetime; } @@ -2657,6 +2661,7 @@ void ioport_manager::exit() // close any playback or recording files playback_end(); record_end(); + timecode_end(); } @@ -3428,6 +3433,12 @@ void ioport_manager::playback_end(const char *message) m_playback_accumulated_speed /= m_playback_accumulated_frames; osd_printf_info("Total playback frames: %d\n", UINT32(m_playback_accumulated_frames)); osd_printf_info("Average recorded speed: %d%%\n", UINT32((m_playback_accumulated_speed * 200 + 1) >> 21)); + + // Close the Mame at the end of inp file playback + //if (strcmp(message, "End of file")) { + osd_printf_info("Exiting MAME now...\n"); + machine().schedule_exit(); + //} } } @@ -3510,6 +3521,29 @@ void ioport_manager::record_write(bool value) record_write(byte); } +template +void ioport_manager::timecode_write(_Type value) +{ + // protect against NULL handles if previous reads fail + if (!m_timecode_file.is_open()) + return; + + // read the value; if we fail, end playback + if (m_timecode_file.write(&value, sizeof(value)) != sizeof(value)) + timecode_end("Out of space"); +} + +/*template<> +void ioport_manager::timecode_write(bool value) +{ + UINT8 byte = UINT8(value); + timecode_write(byte); +}*/ +template<> +void ioport_manager::timecode_write(std::string value) { + timecode_write(value.c_str()); +} + //------------------------------------------------- // record_init - initialize INP recording @@ -3554,6 +3588,41 @@ void ioport_manager::record_init() } +void ioport_manager::timecode_init() { + // if no file, nothing to do + const char *record_filename = machine().options().record(); + if (record_filename[0] == 0) { + machine().video().set_timecode_enabled(false); + return; + } + //osd_printf_error("DEBUG FILENAME-1: %s\n", record_filename); + machine().video().set_timecode_enabled(true); + + // open the record file + std::string filename; + filename.append(record_filename).append(".timecode"); + //sprintf(filename, "%s.timecode", record_filename); + + //osd_printf_error("DEBUG FILENAME-2: %s\n", filename.c_str()); + + file_error filerr = m_timecode_file.open(filename.c_str()); + assert_always(filerr == FILERR_NONE, "Failed to open file for timecode recording"); + + m_timecode_file.puts(std::string("# ==========================================\n").c_str()); + m_timecode_file.puts(std::string("# TIMECODE FILE FOR VIDEO PREVIEW GENERATION\n").c_str()); + m_timecode_file.puts(std::string("# ==========================================\n").c_str()); + m_timecode_file.puts(std::string("#\n").c_str()); + m_timecode_file.puts(std::string("# VIDEO_PART: code of video timecode\n").c_str()); + m_timecode_file.puts(std::string("# START: start time (hh:mm:ss.mmm)\n").c_str()); + m_timecode_file.puts(std::string("# ELAPSED: elapsed time (hh:mm:ss.mmm)\n").c_str()); + m_timecode_file.puts(std::string("# MSEC_START: start time (milliseconds)\n").c_str()); + m_timecode_file.puts(std::string("# MSEC_ELAPSED: elapsed time (milliseconds)\n").c_str()); + m_timecode_file.puts(std::string("# FRAME_START: start time (frames)\n").c_str()); + m_timecode_file.puts(std::string("# FRAME_ELAPSED: elapsed time (frames)\n").c_str()); + m_timecode_file.puts(std::string("#\n").c_str()); + m_timecode_file.puts(std::string("# VIDEO_PART======= START======= ELAPSED===== MSEC_START===== MSEC_ELAPSED=== FRAME_START==== FRAME_ELAPSED==\n").c_str()); +} + //------------------------------------------------- // record_end - end INP recording //------------------------------------------------- @@ -3573,6 +3642,19 @@ void ioport_manager::record_end(const char *message) } +void ioport_manager::timecode_end(const char *message) +{ + // only applies if we have a live file + if (m_timecode_file.is_open()) { + // close the file + m_timecode_file.close(); + + // pop a message + if (message != nullptr) + machine().popmessage("Recording Timecode Ended\nReason: %s", message); + } +} + //------------------------------------------------- // record_frame - start of frame callback for // recording @@ -3584,12 +3666,123 @@ void ioport_manager::record_frame(const attotime &curtime) if (m_record_file.is_open()) { // first the absolute time - record_write(curtime.seconds()); - record_write(curtime.attoseconds()); + record_write(curtime.m_seconds); + record_write(curtime.m_attoseconds); // then the current speed record_write(UINT32(machine().video().speed_percent() * double(1 << 20))); } + + if (m_timecode_file.is_open() && machine().video().get_timecode_write()) { + // Display the timecode + std::string current_time_str; + m_timecode_count++; + strcatprintf(current_time_str, "%02d:%02d:%02d.%03d", + (int)curtime.m_seconds / (60 * 60), + (curtime.m_seconds / 60) % 60, + curtime.m_seconds % 60, + (int)(curtime.m_attoseconds/ATTOSECONDS_PER_MILLISECOND)); + + // Elapsed from previous timecode + attotime elapsed_time = curtime - m_timecode_last_time; + m_timecode_last_time = curtime; + std::string elapsed_time_str; + strcatprintf(elapsed_time_str, "%02d:%02d:%02d.%03d", + elapsed_time.m_seconds / (60 * 60), + (elapsed_time.m_seconds / 60) % 60, + elapsed_time.m_seconds % 60, + int(elapsed_time.m_attoseconds/ATTOSECONDS_PER_MILLISECOND)); + + // Number of ms from beginning of playback + int mseconds_start = curtime.m_seconds*1000 + curtime.m_attoseconds/ATTOSECONDS_PER_MILLISECOND; + std::string mseconds_start_str; + strcatprintf(mseconds_start_str, "%015d", mseconds_start); + + // Number of ms from previous timecode + int mseconds_elapsed = elapsed_time.m_seconds*1000 + elapsed_time.m_attoseconds/ATTOSECONDS_PER_MILLISECOND; + std::string mseconds_elapsed_str; + strcatprintf(mseconds_elapsed_str, "%015d", mseconds_elapsed); + + // Number of frames from beginning of playback + int frame_start = mseconds_start * 60 / 1000; + std::string frame_start_str; + strcatprintf(frame_start_str, "%015d", frame_start); + + // Number of frames from previous timecode + int frame_elapsed = mseconds_elapsed * 60 / 1000; + std::string frame_elapsed_str; + strcatprintf(frame_elapsed_str, "%015d", frame_elapsed); + + std::string messaggio; + std::string timecode_text; + std::string timecode_key; + bool show_timecode_counter = false; + if (m_timecode_count==1) { + messaggio += "INTRO STARTED AT " + current_time_str; + timecode_key = "INTRO_START"; + timecode_text = "INTRO"; + show_timecode_counter = true; + } + else if (m_timecode_count==2) { + messaggio += "INTRO DURATION " + elapsed_time_str; + timecode_key = "INTRO_STOP"; + machine().video().add_to_total_time(elapsed_time); + //timecode_text += "INTRO"; + } + else if (m_timecode_count==3) { + messaggio += "GAMEPLAY STARTED AT " + current_time_str; + timecode_key = "GAMEPLAY_START"; + timecode_text += "GAMEPLAY"; + show_timecode_counter = true; + } + else if (m_timecode_count==4) { + messaggio += "GAMEPLAY DURATION " + elapsed_time_str; + timecode_key = "GAMEPLAY_STOP"; + machine().video().add_to_total_time(elapsed_time); + //timecode_text += "GAMEPLAY"; + } + else if (m_timecode_count % 2 == 1) { + std::string timecode_count_str; + strcatprintf(timecode_count_str, "%03d", (m_timecode_count-3)/2); + timecode_key = "EXTRA_START_" + timecode_count_str; + timecode_count_str.clear(); + strcatprintf(timecode_count_str, "%d", (m_timecode_count-3)/2); + messaggio += "EXTRA " + timecode_count_str + " STARTED AT " + current_time_str; + timecode_text += "EXTRA " + timecode_count_str; + show_timecode_counter = true; + } + else { + machine().video().add_to_total_time(elapsed_time); + + std::string timecode_count_str; + strcatprintf(timecode_count_str, "%d", (m_timecode_count-4)/2); + messaggio += "EXTRA " + timecode_count_str + " DURATION " + elapsed_time_str; + + //std::string timecode_count_str; + timecode_count_str.clear(); + strcatprintf(timecode_count_str, "%03d", (m_timecode_count-4)/2); + timecode_key = "EXTRA_STOP_" + timecode_count_str; + } + + osd_printf_info("%s \n", messaggio.c_str()); + machine().popmessage("%s \n", messaggio.c_str()); + + std::string riga_file; + riga_file.append(timecode_key).append(19-timecode_key.length(), ' '); + //riga_file += "INTRO_START " + + riga_file += + " " + current_time_str + " " + elapsed_time_str + + " " + mseconds_start_str + " " + mseconds_elapsed_str + + " " + frame_start_str + " " + frame_elapsed_str + + "\n"; + m_timecode_file.puts(riga_file.c_str()); + + machine().video().set_timecode_write(false); + //machine().video().set_timecode_text(timecode_text); + machine().video().set_timecode_text(timecode_text); + machine().video().set_timecode_start(m_timecode_last_time); + machine().ui().set_show_timecode_counter(show_timecode_counter); + } } diff --git a/src/emu/ioport.h b/src/emu/ioport.h index 9ba2818c5d0..3fbff3e852c 100644 --- a/src/emu/ioport.h +++ b/src/emu/ioport.h @@ -1452,6 +1452,10 @@ private: void record_frame(const attotime &curtime); void record_port(ioport_port &port); + template void timecode_write(_Type value); + void timecode_init(); + void timecode_end(const char *message = NULL); + // internal state running_machine & m_machine; // reference to owning machine bool m_safe_to_read; // clear at start; set after state is loaded @@ -1474,6 +1478,9 @@ private: emu_file m_playback_file; // playback file (NULL if not recording) UINT64 m_playback_accumulated_speed; // accumulated speed during playback UINT32 m_playback_accumulated_frames; // accumulated frames during playback + emu_file m_timecode_file; // timecode/frames playback file (NULL if not recording) + int m_timecode_count; + attotime m_timecode_last_time; // has... bool m_has_configs; diff --git a/src/emu/ui/ui.cpp b/src/emu/ui/ui.cpp index 6d996ac50a0..860da34e212 100644 --- a/src/emu/ui/ui.cpp +++ b/src/emu/ui/ui.cpp @@ -286,6 +286,8 @@ void ui_manager::init() m_popup_text_end = 0; m_use_natural_keyboard = false; m_mouse_arrow_texture = nullptr; + m_show_timecode_counter = false; + m_show_timecode_total = false; m_load_save_hold = false; get_font_rows(&machine()); @@ -1035,6 +1037,16 @@ bool ui_manager::is_menu_active(void) } +bool ui_manager::show_timecode_counter() +{ + return m_show_timecode_counter; +} +bool ui_manager::show_timecode_total() +{ + return m_show_timecode_total; +} + + /*************************************************************************** TEXT GENERATORS @@ -1559,6 +1571,20 @@ UINT32 ui_manager::handler_ingame(running_machine &machine, render_container *co JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, nullptr, nullptr); } + // Show the duration of current part (intro or gameplay or extra) + if (machine.ui().show_timecode_counter()) { + std::string tempstring; + machine.ui().draw_text_full(container, machine.video().timecode_text(tempstring).c_str(), 0.0f, 0.0f, 1.0f, + JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, rgb_t(0xf0,0xf0,0x10,0x10), ARGB_BLACK, NULL, NULL); + } + // Show the total time elapsed for the video preview (all parts intro, gameplay, extras) + if (machine.ui().show_timecode_total()) { + std::string tempstring; + machine.ui().draw_text_full(container, machine.video().timecode_total_text(tempstring).c_str(), 0.0f, 0.0f, 1.0f, + JUSTIFY_LEFT, WRAP_WORD, DRAW_OPAQUE, rgb_t(0xf0,0x10,0xf0,0x10), ARGB_BLACK, NULL, NULL); + } + + // draw the profiler if visible if (machine.ui().show_profiler()) { diff --git a/src/emu/ui/ui.h b/src/emu/ui/ui.h index 5a07a0800b5..3da9bfb1bab 100644 --- a/src/emu/ui/ui.h +++ b/src/emu/ui/ui.h @@ -171,6 +171,10 @@ public: // other void process_natural_keyboard(); + void set_show_timecode_counter(bool value) { m_show_timecode_counter = value; m_show_timecode_total = true; } + bool show_timecode_counter(); + bool show_timecode_total(); + // word wrap void wrap_text(render_container *container, const char *origs, float x, float y, float origwrapwidth, int &totallines, std::vector &xstart, std::vector &xend, float text_size = 1.0f); @@ -195,6 +199,8 @@ private: std::unique_ptr m_non_char_keys_down; render_texture * m_mouse_arrow_texture; bool m_mouse_show; + bool m_show_timecode_counter; + bool m_show_timecode_total; bool m_load_save_hold; ui_options m_ui_options; diff --git a/src/emu/video.cpp b/src/emu/video.cpp index d05a1b8327c..4b2a315d75c 100644 --- a/src/emu/video.cpp +++ b/src/emu/video.cpp @@ -108,7 +108,13 @@ video_manager::video_manager(running_machine &machine) m_avi_frame_period(attotime::zero), m_avi_next_frame_time(attotime::zero), m_avi_frame(0), - m_dummy_recording(false) + m_dummy_recording(false), + m_timecode_enabled(false), + m_timecode_write(false), + m_timecode_text(""), + m_timecode_start(attotime::zero), + m_timecode_total(attotime::zero) + { // request a callback upon exiting machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(video_manager::exit), this)); @@ -335,6 +341,13 @@ void video_manager::save_snapshot(screen_device *screen, emu_file &file) void video_manager::save_active_screen_snapshots() { + // If record inp is acrive, no snapshot will be created + if (m_timecode_enabled) { + // This flag will write the line on file inp.timecode (see function ioport_manager::record_frame) + m_timecode_write = true; + return; + } + // if we're native, then write one snapshot per visible screen if (m_snap_native) { @@ -360,6 +373,48 @@ void video_manager::save_active_screen_snapshots() } } +std::string &video_manager::timecode_text(std::string &str) { + str.clear(); + str += " "; + + if (!m_timecode_text.empty()) { + str += m_timecode_text + " "; + } + + attotime elapsed_time = machine().time() - m_timecode_start; + std::string elapsed_time_str; + strcatprintf(elapsed_time_str, "%02d:%02d", + (elapsed_time.m_seconds / 60) % 60, + elapsed_time.m_seconds % 60); + str += elapsed_time_str; + + bool paused = machine().paused(); + if (paused) { + str.append(" [paused]"); + } + + str += " "; + + return str; +} + +std::string &video_manager::timecode_total_text(std::string &str) { + str.clear(); + str += " TOTAL "; + + attotime elapsed_time = m_timecode_total; + if (machine().ui().show_timecode_counter()) { + elapsed_time += machine().time() - m_timecode_start; + } + std::string elapsed_time_str; + strcatprintf(elapsed_time_str, "%02d:%02d", + (elapsed_time.m_seconds / 60) % 60, + elapsed_time.m_seconds % 60); + str += elapsed_time_str + " "; + return str; +} + + //------------------------------------------------- // begin_recording - begin recording of a movie diff --git a/src/emu/video.h b/src/emu/video.h index aef70a57f15..2403fe295f6 100644 --- a/src/emu/video.h +++ b/src/emu/video.h @@ -92,6 +92,17 @@ public: void begin_recording(const char *name, movie_format format); void end_recording(movie_format format); void add_sound_to_recording(const INT16 *sound, int numsamples); + + void set_timecode_enabled(bool value) { m_timecode_enabled = value; } + bool get_timecode_enabled() { return m_timecode_enabled; } + bool get_timecode_write() { return m_timecode_write; } + void set_timecode_write(bool value) { m_timecode_write = value; } + void set_timecode_text(std::string &str) { m_timecode_text = str; } + void set_timecode_start(attotime time) { m_timecode_start = time; } + void add_to_total_time(attotime time) { m_timecode_total += time; } + std::string &timecode_text(std::string &str); + std::string &timecode_total_text(std::string &str); + private: // internal helpers @@ -184,6 +195,13 @@ private: static const attoseconds_t ATTOSECONDS_PER_SPEED_UPDATE = ATTOSECONDS_PER_SECOND / 4; static const int PAUSED_REFRESH_RATE = 30; + + bool m_timecode_enabled; // inp.timecode record enabled + bool m_timecode_write; // Show/hide timer at right (partial time) + std::string m_timecode_text; // Message for that video part (intro, gameplay, extra) + attotime m_timecode_start; // Starting timer for that video part (intro, gameplay, extra) + attotime m_timecode_total; // Show/hide timer at left (total elapsed on resulting video preview) + }; #endif /* __VIDEO_H__ */