Ejemplo n.º 1
0
namespace DVDThread
{
struct ReadRequest
{
  bool copy_to_ram;
  u32 output_address;
  u64 dvd_offset;
  u32 length;
  DiscIO::Partition partition;

  // This determines which code DVDInterface will run to reply
  // to the emulated software. We can't use callbacks,
  // because function pointers can't be stored in savestates.
  DVDInterface::ReplyType reply_type;

  // IDs are used to uniquely identify a request. They must not be
  // identical to IDs of any other requests that currently exist, but
  // it's fine to re-use IDs of requests that have existed in the past.
  u64 id;

  // Only used for logging
  u64 time_started_ticks;
  u64 realtime_started_us;
  u64 realtime_done_us;
};

using ReadResult = std::pair<ReadRequest, std::vector<u8>>;

static void StartDVDThread();
static void StopDVDThread();

static void DVDThread();
static void WaitUntilIdle();

static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
                              const DiscIO::Partition& partition,
                              DVDInterface::ReplyType reply_type, s64 ticks_until_completion);

static void FinishRead(u64 id, s64 cycles_late);
static CoreTiming::EventType* s_finish_read;

static u64 s_next_id = 0;

static std::thread s_dvd_thread;
static Common::Event s_request_queue_expanded;    // Is set by CPU thread
static Common::Event s_result_queue_expanded;     // Is set by DVD thread
static Common::Flag s_dvd_thread_exiting(false);  // Is set by CPU thread

static Common::SPSCQueue<ReadRequest, false> s_request_queue;
static Common::SPSCQueue<ReadResult, false> s_result_queue;
static std::map<u64, ReadResult> s_result_map;

static std::unique_ptr<DiscIO::Volume> s_disc;

void Start()
{
  s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead);

  s_request_queue_expanded.Reset();
  s_result_queue_expanded.Reset();
  s_request_queue.Clear();
  s_result_queue.Clear();

  // This is reset on every launch for determinism, but it doesn't matter
  // much, because this will never get exposed to the emulated game.
  s_next_id = 0;

  StartDVDThread();
}

static void StartDVDThread()
{
  ASSERT(!s_dvd_thread.joinable());
  s_dvd_thread_exiting.Clear();
  s_dvd_thread = std::thread(DVDThread);
}

void Stop()
{
  StopDVDThread();
  s_disc.reset();
}

static void StopDVDThread()
{
  ASSERT(s_dvd_thread.joinable());

  // By setting s_DVD_thread_exiting, we ask the DVD thread to cleanly exit.
  // In case the request queue is empty, we need to set s_request_queue_expanded
  // so that the DVD thread will wake up and check s_DVD_thread_exiting.
  s_dvd_thread_exiting.Set();
  s_request_queue_expanded.Set();

  s_dvd_thread.join();
}

void DoState(PointerWrap& p)
{
  // By waiting for the DVD thread to be done working, we ensure
  // that s_request_queue will be empty and that the DVD thread
  // won't be touching anything while this function runs.
  WaitUntilIdle();

  // Move all results from s_result_queue to s_result_map because
  // PointerWrap::Do supports std::map but not Common::SPSCQueue.
  // This won't affect the behavior of FinishRead.
  ReadResult result;
  while (s_result_queue.Pop(result))
    s_result_map.emplace(result.first.id, std::move(result));

  // Both queues are now empty, so we don't need to savestate them.
  p.Do(s_result_map);
  p.Do(s_next_id);

  // s_disc isn't savestated (because it points to files on the
  // local system). Instead, we check that the status of the disc
  // is the same as when the savestate was made. This won't catch
  // cases of having the wrong disc inserted, though.
  // TODO: Check the game ID, disc number, revision?
  bool had_disc = HasDisc();
  p.Do(had_disc);
  if (had_disc != HasDisc())
  {
    if (had_disc)
      PanicAlertT("An inserted disc was expected but not found.");
    else
      s_disc.reset();
  }

  // TODO: Savestates can be smaller if the buffers of results aren't saved,
  // but instead get re-read from the disc when loading the savestate.

  // TODO: It would be possible to create a savestate faster by stopping
  // the DVD thread regardless of whether there are pending requests.

  // After loading a savestate, the debug log in FinishRead will report
  // screwed up times for requests that were submitted before the savestate
  // was made. Handling that properly may be more effort than it's worth.
}

void SetDisc(std::unique_ptr<DiscIO::Volume> disc)
{
  WaitUntilIdle();
  s_disc = std::move(disc);
}

bool HasDisc()
{
  return s_disc != nullptr;
}

bool IsEncryptedAndHashed()
{
  // IsEncryptedAndHashed is thread-safe, so calling WaitUntilIdle isn't necessary.
  return s_disc->IsEncryptedAndHashed();
}

DiscIO::Platform GetDiscType()
{
  // GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary.
  return s_disc->GetVolumeType();
}

u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition)
{
  // PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary.
  return s_disc->PartitionOffsetToRawOffset(offset, partition);
}

IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition)
{
  WaitUntilIdle();
  return s_disc->GetTMD(partition);
}

IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition)
{
  WaitUntilIdle();
  return s_disc->GetTicket(partition);
}

bool IsInsertedDiscRunning()
{
  if (!s_disc)
    return false;

  WaitUntilIdle();

  return SConfig::GetInstance().GetGameID() == s_disc->GetGameID();
}

bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, std::optional<u64> title_id)
{
  if (!s_disc)
    return false;

  WaitUntilIdle();

  if (title_id)
  {
    const std::optional<u64> volume_title_id = s_disc->GetTitleID(partition);
    if (!volume_title_id || *volume_title_id != *title_id)
      return false;
  }

  SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition);
  return true;
}

void WaitUntilIdle()
{
  ASSERT(Core::IsCPUThread());

  while (!s_request_queue.Empty())
    s_result_queue_expanded.Wait();

  StopDVDThread();
  StartDVDThread();
}

void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
               DVDInterface::ReplyType reply_type, s64 ticks_until_completion)
{
  StartReadInternal(false, 0, dvd_offset, length, partition, reply_type, ticks_until_completion);
}

void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length,
                            const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type,
                            s64 ticks_until_completion)
{
  StartReadInternal(true, output_address, dvd_offset, length, partition, reply_type,
                    ticks_until_completion);
}

static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
                              const DiscIO::Partition& partition,
                              DVDInterface::ReplyType reply_type, s64 ticks_until_completion)
{
  ASSERT(Core::IsCPUThread());

  ReadRequest request;

  request.copy_to_ram = copy_to_ram;
  request.output_address = output_address;
  request.dvd_offset = dvd_offset;
  request.length = length;
  request.partition = partition;
  request.reply_type = reply_type;

  u64 id = s_next_id++;
  request.id = id;

  request.time_started_ticks = CoreTiming::GetTicks();
  request.realtime_started_us = Common::Timer::GetTimeUs();

  s_request_queue.Push(std::move(request));
  s_request_queue_expanded.Set();

  CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read, id);
}

static void FinishRead(u64 id, s64 cycles_late)
{
  // We can't simply pop s_result_queue and always get the ReadResult
  // we want, because the DVD thread may add ReadResults to the queue
  // in a different order than we want to get them. What we do instead
  // is to pop the queue until we find the ReadResult we want (the one
  // whose ID matches userdata), which means we may end up popping
  // ReadResults that we don't want. We can't add those unwanted results
  // back to the queue, because the queue can only have one writer.
  // Instead, we add them to a map that only is used by the CPU thread.
  // When this function is called again later, it will check the map for
  // the wanted ReadResult before it starts searching through the queue.
  ReadResult result;
  auto it = s_result_map.find(id);
  if (it != s_result_map.end())
  {
    result = std::move(it->second);
    s_result_map.erase(it);
  }
  else
  {
    while (true)
    {
      while (!s_result_queue.Pop(result))
        s_result_queue_expanded.Wait();

      if (result.first.id == id)
        break;
      else
        s_result_map.emplace(result.first.id, std::move(result));
    }
  }
  // We have now obtained the right ReadResult.

  const ReadRequest& request = result.first;
  const std::vector<u8>& buffer = result.second;

  DEBUG_LOG(DVDINTERFACE,
            "Disc has been read. Real time: %" PRIu64 " us. "
            "Real time including delay: %" PRIu64 " us. "
            "Emulated time including delay: %" PRIu64 " us.",
            request.realtime_done_us - request.realtime_started_us,
            Common::Timer::GetTimeUs() - request.realtime_started_us,
            (CoreTiming::GetTicks() - request.time_started_ticks) /
                (SystemTimers::GetTicksPerSecond() / 1000000));

  if (buffer.size() != request.length)
  {
    PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").",
                request.dvd_offset, request.dvd_offset + request.length);
  }
  else
  {
    if (request.copy_to_ram)
      Memory::CopyToEmu(request.output_address, buffer.data(), request.length);
  }

  // Notify the emulated software that the command has been executed
  DVDInterface::FinishExecutingCommand(request.reply_type, DVDInterface::INT_TCINT, cycles_late,
                                       buffer);
}

static void DVDThread()
{
  Common::SetCurrentThreadName("DVD thread");

  while (true)
  {
    s_request_queue_expanded.Wait();

    if (s_dvd_thread_exiting.IsSet())
      return;

    ReadRequest request;
    while (s_request_queue.Pop(request))
    {
      FileMonitor::Log(*s_disc, request.partition, request.dvd_offset);

      std::vector<u8> buffer(request.length);
      if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition))
        buffer.resize(0);

      request.realtime_done_us = Common::Timer::GetTimeUs();

      s_result_queue.Push(ReadResult(std::move(request), std::move(buffer)));
      s_result_queue_expanded.Set();

      if (s_dvd_thread_exiting.IsSet())
        return;
    }
  }
}
}
Ejemplo n.º 2
0
 ~scoped_thread()
 {
     t.join();
 }
Ejemplo n.º 3
0
static void StartDVDThread()
{
  ASSERT(!s_dvd_thread.joinable());
  s_dvd_thread_exiting.Clear();
  s_dvd_thread = std::thread(DVDThread);
}
Ejemplo n.º 4
0
void CBackupDeleter::waitDone()
{
	if (_thread.joinable())
		_thread.join();
}
	void join() { internal_thread.join(); }
Ejemplo n.º 6
0
	void joinInputThread()
	{
		mShouldBeRunning = false;
		if( mInputThread.joinable() )
			mInputThread.join();
	}
Ejemplo n.º 7
0
void CBackupStatusUpdater::waitDone()
{
	if (_thread.joinable())
		_thread.join();
}
Ejemplo n.º 8
0
 std::thread::id
 get_id() const
 {
     return t_.get_id();
 }
Ejemplo n.º 9
0
 void
 join()
 {
     t_.join();
     s_->propagate_abort();
 }
Ejemplo n.º 10
0
void Flush()
{
	// If already saving state, wait for it to finish
	if (g_save_thread.joinable())
		g_save_thread.join();
}
Ejemplo n.º 11
0
 bool
 joinable() const
 {
     return t_.joinable();
 }
Ejemplo n.º 12
0
namespace State
{

#if defined(__LZO_STRICT_16BIT)
static const u32 IN_LEN = 8 * 1024u;
#elif defined(LZO_ARCH_I086) && !defined(LZO_HAVE_MM_HUGE_ARRAY)
static const u32 IN_LEN = 60 * 1024u;
#else
static const u32 IN_LEN = 128 * 1024u;
#endif

static const u32 OUT_LEN = IN_LEN + (IN_LEN / 16) + 64 + 3;

static unsigned char __LZO_MMODEL out[OUT_LEN];

#define HEAP_ALLOC(var, size) \
	lzo_align_t __LZO_MMODEL var[((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)]

static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);

static std::string g_last_filename;

static CallbackFunc g_onAfterLoadCb = nullptr;

// Temporary undo state buffer
static std::vector<u8> g_undo_load_buffer;
static std::vector<u8> g_current_buffer;
static int g_loadDepth = 0;

static std::mutex g_cs_undo_load_buffer;
static std::mutex g_cs_current_buffer;
static Common::Event g_compressAndDumpStateSyncEvent;

static std::thread g_save_thread;

// Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 43;	// Last changed in PR 2232

// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
// beacuse they save the exact Dolphin version to savestates.
static const std::map<u32, std::pair<std::string, std::string>> s_old_versions =
{
	// The 16 -> 17 change modified the size of StateHeader,
	// so version older than that can't even be decompressed anymore
	{ 17, { "3.5-1311", "3.5-1364" } },
	{ 18, { "3.5-1366", "3.5-1371" } },
	{ 19, { "3.5-1372", "3.5-1408" } },
	{ 20, { "3.5-1409", "4.0-704" } },
	{ 21, { "4.0-705", "4.0-889" } },
	{ 22, { "4.0-905", "4.0-1871" } },
	{ 23, { "4.0-1873", "4.0-1900" } },
	{ 24, { "4.0-1902", "4.0-1919" } },
	{ 25, { "4.0-1921", "4.0-1936" } },
	{ 26, { "4.0-1939", "4.0-1959" } },
	{ 27, { "4.0-1961", "4.0-2018" } },
	{ 28, { "4.0-2020", "4.0-2291" } },
	{ 29, { "4.0-2293", "4.0-2360" } },
	{ 30, { "4.0-2362", "4.0-2628" } },
	{ 31, { "4.0-2632", "4.0-3331" } },
	{ 32, { "4.0-3334", "4.0-3340" } },
	{ 33, { "4.0-3342", "4.0-3373" } },
	{ 34, { "4.0-3376", "4.0-3402" } },
	{ 35, { "4.0-3409", "4.0-3603" } },
	{ 36, { "4.0-3610", "4.0-4480" } },
	{ 37, { "4.0-4484", "4.0-4943" } },
	{ 38, { "4.0-4963", "4.0-5267" } },
	{ 39, { "4.0-5279", "4.0-5525" } },
	{ 40, { "4.0-5531", "4.0-5809" } },
	{ 41, { "4.0-5811", "4.0-5923" } },
	{ 42, { "4.0-5925", "4.0-5946" } }
};

enum
{
	STATE_NONE = 0,
	STATE_SAVE = 1,
	STATE_LOAD = 2,
};

static bool g_use_compression = true;

void EnableCompression(bool compression)
{
	g_use_compression = compression;
}

static std::string DoState(PointerWrap& p)
{
	u32 version = STATE_VERSION;
	{
		static const u32 COOKIE_BASE = 0xBAADBABE;
		u32 cookie = version + COOKIE_BASE;
		p.Do(cookie);
		version = cookie - COOKIE_BASE;
	}

	std::string version_created_by = scm_rev_str;
	if (version > 42)
		p.Do(version_created_by);
	else
		version_created_by.clear();

	if (version != STATE_VERSION)
	{
		if (version_created_by.empty() && s_old_versions.count(version))
		{
			// The savestate is from an old version that doesn't
			// save the Dolphin version number to savestates, but
			// by looking up the savestate version number, it is possible
			// to know approximately which Dolphin version was used.

			std::pair<std::string, std::string> version_range = s_old_versions.find(version)->second;
			std::string oldest_version = version_range.first;
			std::string newest_version = version_range.second;

			version_created_by = "Dolphin " + oldest_version + " - " + newest_version;
		}

		// because the version doesn't match, fail.
		// this will trigger an OSD message like "Can't load state from other revisions"
		// we could use the version numbers to maintain some level of backward compatibility, but currently don't.
		p.SetMode(PointerWrap::MODE_MEASURE);
		return version_created_by;
	}

	p.DoMarker("Version");

	// Begin with video backend, so that it gets a chance to clear its caches and writeback modified things to RAM
	g_video_backend->DoState(p);
	p.DoMarker("video_backend");

	if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
		Wiimote::DoState(p.GetPPtr(), p.GetMode());
	p.DoMarker("Wiimote");

	PowerPC::DoState(p);
	p.DoMarker("PowerPC");
	HW::DoState(p);
	p.DoMarker("HW");
	CoreTiming::DoState(p);
	p.DoMarker("CoreTiming");
	Movie::DoState(p);
	p.DoMarker("Movie");

#if defined(HAVE_LIBAV) || defined (WIN32)
	AVIDump::DoState();
#endif

	return version_created_by;
}

void LoadFromBuffer(std::vector<u8>& buffer)
{
	bool wasUnpaused = Core::PauseAndLock(true);

	u8* ptr = &buffer[0];
	PointerWrap p(&ptr, PointerWrap::MODE_READ);
	DoState(p);

	Core::PauseAndLock(false, wasUnpaused);
}

void SaveToBuffer(std::vector<u8>& buffer)
{
	bool wasUnpaused = Core::PauseAndLock(true);

	u8* ptr = nullptr;
	PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);

	DoState(p);
	const size_t buffer_size = reinterpret_cast<size_t>(ptr);
	buffer.resize(buffer_size);

	ptr = &buffer[0];
	p.SetMode(PointerWrap::MODE_WRITE);
	DoState(p);

	Core::PauseAndLock(false, wasUnpaused);
}

void VerifyBuffer(std::vector<u8>& buffer)
{
	bool wasUnpaused = Core::PauseAndLock(true);

	u8* ptr = &buffer[0];
	PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
	DoState(p);

	Core::PauseAndLock(false, wasUnpaused);
}

// return state number not in map
static int GetEmptySlot(std::map<double, int> m)
{
	for (int i = 1; i <= (int)NUM_STATES; i++)
	{
		bool found = false;
		for (auto& p : m)
		{
			if (p.second == i)
			{
				found = true;
				break;
			}
		}
		if (!found)
			return i;
	}
	return -1;
}

static std::string MakeStateFilename(int number);

// read state timestamps
static std::map<double, int> GetSavedStates()
{
	StateHeader header;
	std::map<double, int> m;
	for (int i = 1; i <= (int)NUM_STATES; i++)
	{
		std::string filename = MakeStateFilename(i);
		if (File::Exists(filename))
		{
			if (ReadHeader(filename, header))
			{
				double d = Common::Timer::GetDoubleTime() - header.time;
				// increase time until unique value is obtained
				while (m.find(d) != m.end()) d += .001;
				m.insert(std::pair<double,int>(d, i));
			}
		}
	}
	return m;
}

struct CompressAndDumpState_args
{
	std::vector<u8>* buffer_vector;
	std::mutex* buffer_mutex;
	std::string filename;
	bool wait;
};

static void CompressAndDumpState(CompressAndDumpState_args save_args)
{
	std::lock_guard<std::mutex> lk(*save_args.buffer_mutex);
	if (!save_args.wait)
		g_compressAndDumpStateSyncEvent.Set();

	const u8* const buffer_data = &(*(save_args.buffer_vector))[0];
	const size_t buffer_size = (save_args.buffer_vector)->size();
	std::string& filename = save_args.filename;

	// For easy debugging
	Common::SetCurrentThreadName("SaveState thread");

	// Moving to last overwritten save-state
	if (File::Exists(filename))
	{
		if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav"))
			File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav"));
		if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav.dtm"))
			File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav.dtm"));

		if (!File::Rename(filename, File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav"))
			Core::DisplayMessage("Failed to move previous state to state undo backup", 1000);
		else
			File::Rename(filename + ".dtm", File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav.dtm");
	}

	if ((Movie::IsMovieActive()) && !Movie::IsJustStartingRecordingInputFromSaveState())
		Movie::SaveRecording(filename + ".dtm");
	else if (!Movie::IsMovieActive())
		File::Delete(filename + ".dtm");

	File::IOFile f(filename, "wb");
	if (!f)
	{
		Core::DisplayMessage("Could not save state", 2000);
		g_compressAndDumpStateSyncEvent.Set();
		return;
	}

	// Setting up the header
	StateHeader header;
	memcpy(header.gameID, SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), 6);
	header.size = g_use_compression ? (u32)buffer_size : 0;
	header.time = Common::Timer::GetDoubleTime();

	f.WriteArray(&header, 1);

	if (header.size != 0) // non-zero header size means the state is compressed
	{
		lzo_uint i = 0;
		while (true)
		{
			lzo_uint32 cur_len = 0;
			lzo_uint out_len = 0;

			if ((i + IN_LEN) >= buffer_size)
			{
				cur_len = (lzo_uint32)(buffer_size - i);
			}
			else
			{
				cur_len = IN_LEN;
			}

			if (lzo1x_1_compress(buffer_data + i, cur_len, out, &out_len, wrkmem) != LZO_E_OK)
				PanicAlertT("Internal LZO Error - compression failed");

			// The size of the data to write is 'out_len'
			f.WriteArray((lzo_uint32*)&out_len, 1);
			f.WriteBytes(out, out_len);

			if (cur_len != IN_LEN)
				break;

			i += cur_len;
		}
	}
	else // uncompressed
	{
		f.WriteBytes(buffer_data, buffer_size);
	}

	Core::DisplayMessage(StringFromFormat("Saved State to %s", filename.c_str()), 2000);
	g_compressAndDumpStateSyncEvent.Set();
}

void SaveAs(const std::string& filename, bool wait)
{
	// Pause the core while we save the state
	bool wasUnpaused = Core::PauseAndLock(true);

	// Measure the size of the buffer.
	u8 *ptr = nullptr;
	PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
	DoState(p);
	const size_t buffer_size = reinterpret_cast<size_t>(ptr);

	// Then actually do the write.
	{
		std::lock_guard<std::mutex> lk(g_cs_current_buffer);
		g_current_buffer.resize(buffer_size);
		ptr = &g_current_buffer[0];
		p.SetMode(PointerWrap::MODE_WRITE);
		DoState(p);
	}

	if (p.GetMode() == PointerWrap::MODE_WRITE)
	{
		Core::DisplayMessage("Saving State...", 1000);

		CompressAndDumpState_args save_args;
		save_args.buffer_vector = &g_current_buffer;
		save_args.buffer_mutex = &g_cs_current_buffer;
		save_args.filename = filename;
		save_args.wait = wait;

		Flush();
		g_save_thread = std::thread(CompressAndDumpState, save_args);
		g_compressAndDumpStateSyncEvent.Wait();

		g_last_filename = filename;
	}
	else
	{
		// someone aborted the save by changing the mode?
		Core::DisplayMessage("Unable to save: Internal DoState Error", 4000);
	}

	// Resume the core and disable stepping
	Core::PauseAndLock(false, wasUnpaused);
}

bool ReadHeader(const std::string& filename, StateHeader& header)
{
	Flush();
	File::IOFile f(filename, "rb");
	if (!f)
	{
		Core::DisplayMessage("State not found", 2000);
		return false;
	}

	f.ReadArray(&header, 1);
	return true;
}

static void LoadFileStateData(const std::string& filename, std::vector<u8>& ret_data)
{
	Flush();
	File::IOFile f(filename, "rb");
	if (!f)
	{
		Core::DisplayMessage("State not found", 2000);
		return;
	}

	StateHeader header;
	f.ReadArray(&header, 1);

	if (memcmp(SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), header.gameID, 6))
	{
		Core::DisplayMessage(StringFromFormat("State belongs to a different game (ID %.*s)",
			6, header.gameID), 2000);
		return;
	}

	std::vector<u8> buffer;

	if (header.size != 0) // non-zero size means the state is compressed
	{
		Core::DisplayMessage("Decompressing State...", 500);

		buffer.resize(header.size);

		lzo_uint i = 0;
		while (true)
		{
			lzo_uint32 cur_len = 0;  // number of bytes to read
			lzo_uint new_len = 0;  // number of bytes to write

			if (!f.ReadArray(&cur_len, 1))
				break;

			f.ReadBytes(out, cur_len);
			const int res = lzo1x_decompress(out, cur_len, &buffer[i], &new_len, nullptr);
			if (res != LZO_E_OK)
			{
				// This doesn't seem to happen anymore.
				PanicAlertT("Internal LZO Error - decompression failed (%d) (%li, %li) \n"
					"Try loading the state again", res, i, new_len);
				return;
			}

			i += new_len;
		}
	}
	else // uncompressed
	{
		const size_t size = (size_t)(f.GetSize() - sizeof(StateHeader));
		buffer.resize(size);

		if (!f.ReadBytes(&buffer[0], size))
		{
			PanicAlert("wtf? reading bytes: %i", (int)size);
			return;
		}
	}

	// all good
	ret_data.swap(buffer);
}

void LoadAs(const std::string& filename)
{
	if (!Core::IsRunning())
		return;

	// Stop the core while we load the state
	bool wasUnpaused = Core::PauseAndLock(true);

	g_loadDepth++;

	// Save temp buffer for undo load state
	if (!Movie::IsJustStartingRecordingInputFromSaveState())
	{
		std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
		SaveToBuffer(g_undo_load_buffer);
		if (Movie::IsMovieActive())
			Movie::SaveRecording(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
		else if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) +"undo.dtm"))
			File::Delete(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
	}

	bool loaded = false;
	bool loadedSuccessfully = false;
	std::string version_created_by;

	// brackets here are so buffer gets freed ASAP
	{
		std::vector<u8> buffer;
		LoadFileStateData(filename, buffer);

		if (!buffer.empty())
		{
			u8 *ptr = &buffer[0];
			PointerWrap p(&ptr, PointerWrap::MODE_READ);
			version_created_by = DoState(p);
			loaded = true;
			loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ);
		}
	}

	if (loaded)
	{
		if (loadedSuccessfully)
		{
			Core::DisplayMessage(StringFromFormat("Loaded state from %s", filename.c_str()), 2000);
			if (File::Exists(filename + ".dtm"))
				Movie::LoadInput(filename + ".dtm");
			else if (!Movie::IsJustStartingRecordingInputFromSaveState() && !Movie::IsJustStartingPlayingInputFromSaveState())
				Movie::EndPlayInput(false);
		}
		else
		{
			// failed to load
			Core::DisplayMessage("Unable to load: Can't load state from other versions!", 4000);
			if (!version_created_by.empty())
				Core::DisplayMessage("The savestate was created using " + version_created_by, 4000);

			// since we could be in an inconsistent state now (and might crash or whatever), undo.
			if (g_loadDepth < 2)
				UndoLoadState();
		}
	}

	if (g_onAfterLoadCb)
		g_onAfterLoadCb();

	g_loadDepth--;

	// resume dat core
	Core::PauseAndLock(false, wasUnpaused);
}

void SetOnAfterLoadCallback(CallbackFunc callback)
{
	g_onAfterLoadCb = callback;
}

void VerifyAt(const std::string& filename)
{
	bool wasUnpaused = Core::PauseAndLock(true);

	std::vector<u8> buffer;
	LoadFileStateData(filename, buffer);

	if (!buffer.empty())
	{
		u8 *ptr = &buffer[0];
		PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
		DoState(p);

		if (p.GetMode() == PointerWrap::MODE_VERIFY)
			Core::DisplayMessage(StringFromFormat("Verified state at %s", filename.c_str()), 2000);
		else
			Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
	}

	Core::PauseAndLock(false, wasUnpaused);
}


void Init()
{
	if (lzo_init() != LZO_E_OK)
		PanicAlertT("Internal LZO Error - lzo_init() failed");
}

void Shutdown()
{
	Flush();

	// swapping with an empty vector, rather than clear()ing
	// this gives a better guarantee to free the allocated memory right NOW (as opposed to, actually, never)
	{
		std::lock_guard<std::mutex> lk(g_cs_current_buffer);
		std::vector<u8>().swap(g_current_buffer);
	}

	{
		std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
		std::vector<u8>().swap(g_undo_load_buffer);
	}
}

static std::string MakeStateFilename(int number)
{
	return StringFromFormat("%s%s.s%02i", File::GetUserPath(D_STATESAVES_IDX).c_str(),
		SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), number);
}

void Save(int slot, bool wait)
{
	SaveAs(MakeStateFilename(slot), wait);
}

void Load(int slot)
{
	LoadAs(MakeStateFilename(slot));
}

void Verify(int slot)
{
	VerifyAt(MakeStateFilename(slot));
}

void LoadLastSaved(int i)
{
	std::map<double, int> savedStates = GetSavedStates();

	if (i > (int)savedStates.size())
		Core::DisplayMessage("State doesn't exist", 2000);
	else
	{
		std::map<double, int>::iterator it = savedStates.begin();
		std::advance(it, i-1);
		Load(it->second);
	}
}

// must wait for state to be written because it must know if all slots are taken
void SaveFirstSaved()
{
	std::map<double, int> savedStates = GetSavedStates();

	// save to an empty slot
	if (savedStates.size() < NUM_STATES)
		Save(GetEmptySlot(savedStates), true);
	// overwrite the oldest state
	else
	{
		std::map<double, int>::iterator it = savedStates.begin();
		std::advance(it, savedStates.size()-1);
		Save(it->second, true);
	}
}

void Flush()
{
	// If already saving state, wait for it to finish
	if (g_save_thread.joinable())
		g_save_thread.join();
}

// Load the last state before loading the state
void UndoLoadState()
{
	std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
	if (!g_undo_load_buffer.empty())
	{
		if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm") || (!Movie::IsMovieActive()))
		{
			LoadFromBuffer(g_undo_load_buffer);
			if (Movie::IsMovieActive())
				Movie::LoadInput(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
		}
		else
		{
			PanicAlert("No undo.dtm found, aborting undo load state to prevent movie desyncs");
		}
	}
	else
	{
		PanicAlert("There is nothing to undo!");
	}
}

// Load the state that the last save state overwritten on
void UndoSaveState()
{
	LoadAs(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav");
}

} // namespace State
Ejemplo n.º 13
0
static void EmuThreadJoin() {
	emuThread.join();
	emuThread = std::thread();
	ILOG("EmuThreadJoin - joined");
}
Ejemplo n.º 14
0
 explicit scoped_thread(std::thread t_):
     t(std::move(t_))
 {
     if(!t.joinable())
         throw std::logic_error("No thread");
 }
Ejemplo n.º 15
0
// Initialize and create emulation thread
// Call browser: Init():s_emu_thread().
// See the BootManager.cpp file description for a complete call schedule.
void EmuThread()
{
	const SConfig& core_parameter = SConfig::GetInstance();

	Common::SetCurrentThreadName("Emuthread - Starting");

	if (SConfig::GetInstance().m_OCEnable)
		DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000);
	DisplayMessage(cpu_info.brand_string, 8000);
	DisplayMessage(cpu_info.Summarize(), 8000);
	DisplayMessage(core_parameter.m_strFilename, 3000);

	// For a time this acts as the CPU thread...
	DeclareAsCPUThread();

	Movie::Init();

	HW::Init();

	if (!g_video_backend->Initialize(s_window_handle))
	{
		PanicAlert("Failed to initialize video backend!");
		Host_Message(WM_USER_STOP);
		return;
	}

	OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000);

	if (cpu_info.HTT)
		SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 4;
	else
		SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 2;

	if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread))
	{
		HW::Shutdown();
		g_video_backend->Shutdown();
		PanicAlert("Failed to initialize DSP emulation!");
		Host_Message(WM_USER_STOP);
		return;
	}

	bool init_controllers = false;
	if (!g_controller_interface.IsInit())
	{
		Pad::Initialize(s_window_handle);
		Keyboard::Initialize(s_window_handle);
		init_controllers = true;
	}
	else
	{
		// Update references in case controllers were refreshed
		Pad::LoadConfig();
		Keyboard::LoadConfig();
	}

	// Load and Init Wiimotes - only if we are booting in Wii mode
	if (core_parameter.bWii)
	{
		if (init_controllers)
			Wiimote::Initialize(s_window_handle, !s_state_filename.empty());
		else
			Wiimote::LoadConfig();

		// Activate Wiimotes which don't have source set to "None"
		for (unsigned int i = 0; i != MAX_BBMOTES; ++i)
			if (g_wiimote_sources[i])
				GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true);

	}

	AudioCommon::InitSoundStream();

	// The hardware is initialized.
	s_hardware_initialized = true;

	// Boot to pause or not
	Core::SetState(core_parameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN);

	// Load GCM/DOL/ELF whatever ... we boot with the interpreter core
	PowerPC::SetMode(PowerPC::MODE_INTERPRETER);

	CBoot::BootUp();

	// Thread is no longer acting as CPU Thread
	UndeclareAsCPUThread();

	// Setup our core, but can't use dynarec if we are compare server
	if (core_parameter.iCPUCore != PowerPC::CORE_INTERPRETER
	    && (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient))
	{
		PowerPC::SetMode(PowerPC::MODE_JIT);
	}
	else
	{
		PowerPC::SetMode(PowerPC::MODE_INTERPRETER);
	}

	// Update the window again because all stuff is initialized
	Host_UpdateDisasmDialog();
	Host_UpdateMainFrame();

	// Determine the CPU thread function
	void (*cpuThreadFunc)(void);
	if (core_parameter.m_BootType == SConfig::BOOT_DFF)
		cpuThreadFunc = FifoPlayerThread;
	else
		cpuThreadFunc = CpuThread;

	// ENTER THE VIDEO THREAD LOOP
	if (core_parameter.bCPUThread)
	{
		// This thread, after creating the EmuWindow, spawns a CPU
		// thread, and then takes over and becomes the video thread
		Common::SetCurrentThreadName("Video thread");

		g_video_backend->Video_Prepare();

		// Spawn the CPU thread
		s_cpu_thread = std::thread(cpuThreadFunc);

		// become the GPU thread
		g_video_backend->Video_EnterLoop();

		// We have now exited the Video Loop
		INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str());
	}
	else // SingleCore mode
	{
		// The spawned CPU Thread also does the graphics.
		// The EmuThread is thus an idle thread, which sleeps while
		// waiting for the program to terminate. Without this extra
		// thread, the video backend window hangs in single core mode
		// because no one is pumping messages.
		Common::SetCurrentThreadName("Emuthread - Idle");

		// Spawn the CPU+GPU thread
		s_cpu_thread = std::thread(cpuThreadFunc);

		while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN)
		{
			g_video_backend->PeekMessages();
			Common::SleepCurrentThread(20);
		}
	}

	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str());

	// Wait for s_cpu_thread to exit
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str());

	#ifdef USE_GDBSTUB
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str());
	gdb_deinit();
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str());
	#endif

	s_cpu_thread.join();

	INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str());

	if (core_parameter.bCPUThread)
		g_video_backend->Video_Cleanup();

	FileMon::Close();

	// Stop audio thread - Actually this does nothing when using HLE
	// emulation, but stops the DSP Interpreter when using LLE emulation.
	DSP::GetDSPEmulator()->DSP_StopSoundStream();

	// We must set up this flag before executing HW::Shutdown()
	s_hardware_initialized = false;
	INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str());
	HW::Shutdown();
	INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str());

	if (init_controllers)
	{
		Wiimote::Shutdown();
		Keyboard::Shutdown();
		Pad::Shutdown();
		init_controllers = false;
	}

	g_video_backend->Shutdown();
	AudioCommon::ShutdownSoundStream();

	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str());

	// Clear on screen messages that haven't expired
	g_video_backend->Video_ClearMessages();

	// Reload sysconf file in order to see changes committed during emulation
	if (core_parameter.bWii)
		SConfig::GetInstance().m_SYSCONF->Reload();

	INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----");
	Movie::Shutdown();
	PatchEngine::Shutdown();

	s_is_stopping = false;

	if (s_on_stopped_callback)
		s_on_stopped_callback();
}
Ejemplo n.º 16
0
 void
 detach()
 {
     t_.detach();
 }
Ejemplo n.º 17
0
namespace Core
{

// TODO: ugly, remove
bool g_aspect_wide;

bool g_want_determinism;

// Declarations and definitions
static Common::Timer s_timer;
static std::atomic<u32> s_drawn_frame;
static std::atomic<u32> s_drawn_video;

// Function forwarding
void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size);

// Function declarations
void EmuThread();

static bool s_is_stopping = false;
static bool s_hardware_initialized = false;
static bool s_is_started = false;
static void* s_window_handle = nullptr;
static std::string s_state_filename;
static std::thread s_emu_thread;
static StoppedCallbackFunc s_on_stopped_callback = nullptr;

static std::thread s_cpu_thread;
static bool s_request_refresh_info = false;
static int s_pause_and_lock_depth = 0;
static bool s_is_framelimiter_temp_disabled = false;

#ifdef ThreadLocalStorage
static ThreadLocalStorage bool tls_is_cpu_thread = false;
#else
static pthread_key_t s_tls_is_cpu_key;
static pthread_once_t s_cpu_key_is_init = PTHREAD_ONCE_INIT;
static void InitIsCPUKey()
{
	pthread_key_create(&s_tls_is_cpu_key, nullptr);
}
#endif

bool GetIsFramelimiterTempDisabled()
{
	return s_is_framelimiter_temp_disabled;
}

void SetIsFramelimiterTempDisabled(bool disable)
{
	s_is_framelimiter_temp_disabled = disable;
}

std::string GetStateFileName() { return s_state_filename; }
void SetStateFileName(const std::string& val) { s_state_filename = val; }

void FrameUpdateOnCPUThread()
{
	if (NetPlay::IsNetPlayRunning())
		NetPlayClient::SendTimeBase();
}

// Display messages and return values

// Formatted stop message
std::string StopMessage(bool main_thread, const std::string& message)
{
	return StringFromFormat("Stop [%s %i]\t%s\t%s",
		main_thread ? "Main Thread" : "Video Thread", Common::CurrentThreadId(), MemUsage().c_str(), message.c_str());
}

void DisplayMessage(const std::string& message, int time_in_ms)
{
	if (!IsRunning())
		return;

	// Actually displaying non-ASCII could cause things to go pear-shaped
	for (const char& c : message)
	{
		if (!std::isprint(c))
			return;
	}

	g_video_backend->Video_AddMessage(message, time_in_ms);
	Host_UpdateTitle(message);
}

bool IsRunning()
{
	return (GetState() != CORE_UNINITIALIZED || s_hardware_initialized) && !s_is_stopping;
}

bool IsRunningAndStarted()
{
	return s_is_started && !s_is_stopping;
}

bool IsRunningInCurrentThread()
{
	return IsRunning() && IsCPUThread();
}

bool IsCPUThread()
{
#ifdef ThreadLocalStorage
	return tls_is_cpu_thread;
#else
	// Use pthread implementation for Android and Mac
	// Make sure that s_tls_is_cpu_key is initialized
	pthread_once(&s_cpu_key_is_init, InitIsCPUKey);
	return pthread_getspecific(s_tls_is_cpu_key);
#endif
}

bool IsGPUThread()
{
	const SConfig& _CoreParameter = SConfig::GetInstance();
	if (_CoreParameter.bCPUThread)
	{
		return (s_emu_thread.joinable() && (s_emu_thread.get_id() == std::this_thread::get_id()));
	}
	else
	{
		return IsCPUThread();
	}
}

// This is called from the GUI thread. See the booting call schedule in
// BootManager.cpp
bool Init()
{
	const SConfig& _CoreParameter = SConfig::GetInstance();

	if (s_emu_thread.joinable())
	{
		if (IsRunning())
		{
			PanicAlertT("Emu Thread already running");
			return false;
		}

		// The Emu Thread was stopped, synchronize with it.
		s_emu_thread.join();
	}

	Core::UpdateWantDeterminism(/*initial*/ true);

	INFO_LOG(OSREPORT, "Starting core = %s mode",
		_CoreParameter.bWii ? "Wii" : "GameCube");
	INFO_LOG(OSREPORT, "CPU Thread separate = %s",
		_CoreParameter.bCPUThread ? "Yes" : "No");

	Host_UpdateMainFrame(); // Disable any menus or buttons at boot

	g_aspect_wide = _CoreParameter.bWii;
	if (g_aspect_wide)
	{
		IniFile gameIni = _CoreParameter.LoadGameIni();
		gameIni.GetOrCreateSection("Wii")->Get("Widescreen", &g_aspect_wide,
		     !!SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.AR"));
	}

	s_window_handle = Host_GetRenderHandle();

	// Start the emu thread
	s_emu_thread = std::thread(EmuThread);

	return true;
}

// Called from GUI thread
void Stop()  // - Hammertime!
{
	if (GetState() == CORE_STOPPING)
		return;

	const SConfig& _CoreParameter = SConfig::GetInstance();

	s_is_stopping = true;

	g_video_backend->EmuStateChange(EMUSTATE_CHANGE_STOP);

	INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----");

	// Stop the CPU
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stop CPU").c_str());
	PowerPC::Stop();

	// Kick it if it's waiting (code stepping wait loop)
	CPU::StepOpcode();

	if (_CoreParameter.bCPUThread)
	{
		// Video_EnterLoop() should now exit so that EmuThread()
		// will continue concurrently with the rest of the commands
		// in this function. We no longer rely on Postmessage.
		INFO_LOG(CONSOLE, "%s", StopMessage(true, "Wait for Video Loop to exit ...").c_str());

		g_video_backend->Video_ExitLoop();
	}
}

void DeclareAsCPUThread()
{
#ifdef ThreadLocalStorage
	tls_is_cpu_thread = true;
#else
	// Use pthread implementation for Android and Mac
	// Make sure that s_tls_is_cpu_key is initialized
	pthread_once(&s_cpu_key_is_init, InitIsCPUKey);
	pthread_setspecific(s_tls_is_cpu_key, (void*)true);
#endif
}

void UndeclareAsCPUThread()
{
#ifdef ThreadLocalStorage
	tls_is_cpu_thread = false;
#else
	// Use pthread implementation for Android and Mac
	// Make sure that s_tls_is_cpu_key is initialized
	pthread_once(&s_cpu_key_is_init, InitIsCPUKey);
	pthread_setspecific(s_tls_is_cpu_key, (void*)false);
#endif
}

// Create the CPU thread, which is a CPU + Video thread in Single Core mode.
static void CpuThread()
{
	DeclareAsCPUThread();

	const SConfig& _CoreParameter = SConfig::GetInstance();

	if (_CoreParameter.bCPUThread)
	{
		Common::SetCurrentThreadName("CPU thread");
	}
	else
	{
		Common::SetCurrentThreadName("CPU-GPU thread");
		g_video_backend->Video_Prepare();
	}

	if (_CoreParameter.bFastmem)
		EMM::InstallExceptionHandler(); // Let's run under memory watch

	if (!s_state_filename.empty())
		State::LoadAs(s_state_filename);

	s_is_started = true;


	#ifdef USE_GDBSTUB
	#ifndef _WIN32
	if (!_CoreParameter.gdb_socket.empty())
	{
		gdb_init_local(_CoreParameter.gdb_socket.data());
		gdb_break();
	}
	else
	#endif
	if (_CoreParameter.iGDBPort > 0)
	{
		gdb_init(_CoreParameter.iGDBPort);
		// break at next instruction (the first instruction)
		gdb_break();
	}
	#endif

	// Enter CPU run loop. When we leave it - we are done.
	CPU::Run();

	s_is_started = false;

	if (!_CoreParameter.bCPUThread)
		g_video_backend->Video_Cleanup();

	if (_CoreParameter.bFastmem)
		EMM::UninstallExceptionHandler();

	return;
}

static void FifoPlayerThread()
{
	const SConfig& _CoreParameter = SConfig::GetInstance();

	if (_CoreParameter.bCPUThread)
	{
		Common::SetCurrentThreadName("FIFO player thread");
	}
	else
	{
		g_video_backend->Video_Prepare();
		Common::SetCurrentThreadName("FIFO-GPU thread");
	}

	s_is_started = true;
	DeclareAsCPUThread();

	// Enter CPU run loop. When we leave it - we are done.
	if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename))
	{
		FifoPlayer::GetInstance().Play();
		FifoPlayer::GetInstance().Close();
	}

	UndeclareAsCPUThread();
	s_is_started = false;

	if (!_CoreParameter.bCPUThread)
		g_video_backend->Video_Cleanup();

	return;
}

// Initialize and create emulation thread
// Call browser: Init():s_emu_thread().
// See the BootManager.cpp file description for a complete call schedule.
void EmuThread()
{
	const SConfig& core_parameter = SConfig::GetInstance();

	Common::SetCurrentThreadName("Emuthread - Starting");

	if (SConfig::GetInstance().m_OCEnable)
		DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000);
	DisplayMessage(cpu_info.brand_string, 8000);
	DisplayMessage(cpu_info.Summarize(), 8000);
	DisplayMessage(core_parameter.m_strFilename, 3000);

	// For a time this acts as the CPU thread...
	DeclareAsCPUThread();

	Movie::Init();

	HW::Init();

	if (!g_video_backend->Initialize(s_window_handle))
	{
		PanicAlert("Failed to initialize video backend!");
		Host_Message(WM_USER_STOP);
		return;
	}

	OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000);

	if (cpu_info.HTT)
		SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 4;
	else
		SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 2;

	if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread))
	{
		HW::Shutdown();
		g_video_backend->Shutdown();
		PanicAlert("Failed to initialize DSP emulation!");
		Host_Message(WM_USER_STOP);
		return;
	}

	bool init_controllers = false;
	if (!g_controller_interface.IsInit())
	{
		Pad::Initialize(s_window_handle);
		Keyboard::Initialize(s_window_handle);
		init_controllers = true;
	}
	else
	{
		// Update references in case controllers were refreshed
		Pad::LoadConfig();
		Keyboard::LoadConfig();
	}

	// Load and Init Wiimotes - only if we are booting in Wii mode
	if (core_parameter.bWii)
	{
		if (init_controllers)
			Wiimote::Initialize(s_window_handle, !s_state_filename.empty());
		else
			Wiimote::LoadConfig();

		// Activate Wiimotes which don't have source set to "None"
		for (unsigned int i = 0; i != MAX_BBMOTES; ++i)
			if (g_wiimote_sources[i])
				GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true);

	}

	AudioCommon::InitSoundStream();

	// The hardware is initialized.
	s_hardware_initialized = true;

	// Boot to pause or not
	Core::SetState(core_parameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN);

	// Load GCM/DOL/ELF whatever ... we boot with the interpreter core
	PowerPC::SetMode(PowerPC::MODE_INTERPRETER);

	CBoot::BootUp();

	// Thread is no longer acting as CPU Thread
	UndeclareAsCPUThread();

	// Setup our core, but can't use dynarec if we are compare server
	if (core_parameter.iCPUCore != PowerPC::CORE_INTERPRETER
	    && (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient))
	{
		PowerPC::SetMode(PowerPC::MODE_JIT);
	}
	else
	{
		PowerPC::SetMode(PowerPC::MODE_INTERPRETER);
	}

	// Update the window again because all stuff is initialized
	Host_UpdateDisasmDialog();
	Host_UpdateMainFrame();

	// Determine the CPU thread function
	void (*cpuThreadFunc)(void);
	if (core_parameter.m_BootType == SConfig::BOOT_DFF)
		cpuThreadFunc = FifoPlayerThread;
	else
		cpuThreadFunc = CpuThread;

	// ENTER THE VIDEO THREAD LOOP
	if (core_parameter.bCPUThread)
	{
		// This thread, after creating the EmuWindow, spawns a CPU
		// thread, and then takes over and becomes the video thread
		Common::SetCurrentThreadName("Video thread");

		g_video_backend->Video_Prepare();

		// Spawn the CPU thread
		s_cpu_thread = std::thread(cpuThreadFunc);

		// become the GPU thread
		g_video_backend->Video_EnterLoop();

		// We have now exited the Video Loop
		INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str());
	}
	else // SingleCore mode
	{
		// The spawned CPU Thread also does the graphics.
		// The EmuThread is thus an idle thread, which sleeps while
		// waiting for the program to terminate. Without this extra
		// thread, the video backend window hangs in single core mode
		// because no one is pumping messages.
		Common::SetCurrentThreadName("Emuthread - Idle");

		// Spawn the CPU+GPU thread
		s_cpu_thread = std::thread(cpuThreadFunc);

		while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN)
		{
			g_video_backend->PeekMessages();
			Common::SleepCurrentThread(20);
		}
	}

	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str());

	// Wait for s_cpu_thread to exit
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str());

	#ifdef USE_GDBSTUB
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str());
	gdb_deinit();
	INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str());
	#endif

	s_cpu_thread.join();

	INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str());

	if (core_parameter.bCPUThread)
		g_video_backend->Video_Cleanup();

	FileMon::Close();

	// Stop audio thread - Actually this does nothing when using HLE
	// emulation, but stops the DSP Interpreter when using LLE emulation.
	DSP::GetDSPEmulator()->DSP_StopSoundStream();

	// We must set up this flag before executing HW::Shutdown()
	s_hardware_initialized = false;
	INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str());
	HW::Shutdown();
	INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str());

	if (init_controllers)
	{
		Wiimote::Shutdown();
		Keyboard::Shutdown();
		Pad::Shutdown();
		init_controllers = false;
	}

	g_video_backend->Shutdown();
	AudioCommon::ShutdownSoundStream();

	INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str());

	// Clear on screen messages that haven't expired
	g_video_backend->Video_ClearMessages();

	// Reload sysconf file in order to see changes committed during emulation
	if (core_parameter.bWii)
		SConfig::GetInstance().m_SYSCONF->Reload();

	INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----");
	Movie::Shutdown();
	PatchEngine::Shutdown();

	s_is_stopping = false;

	if (s_on_stopped_callback)
		s_on_stopped_callback();
}

// Set or get the running state

void SetState(EState _State)
{
	switch (_State)
	{
	case CORE_PAUSE:
		CPU::EnableStepping(true);  // Break
		Wiimote::Pause();
		break;
	case CORE_RUN:
		CPU::EnableStepping(false);
		Wiimote::Resume();
		break;
	default:
		PanicAlert("Invalid state");
		break;
	}
}

EState GetState()
{
	if (s_is_stopping)
		return CORE_STOPPING;

	if (s_hardware_initialized)
	{
		if (CPU::IsStepping())
			return CORE_PAUSE;

		return CORE_RUN;
	}

	return CORE_UNINITIALIZED;
}

static std::string GenerateScreenshotFolderPath()
{
	const std::string& gameId = SConfig::GetInstance().GetUniqueID();
	std::string path = File::GetUserPath(D_SCREENSHOTS_IDX) + gameId + DIR_SEP_CHR;

	if (!File::CreateFullPath(path))
	{
		// fallback to old-style screenshots, without folder.
		path = File::GetUserPath(D_SCREENSHOTS_IDX);
	}

	return path;
}

static std::string GenerateScreenshotName()
{
	std::string path = GenerateScreenshotFolderPath();

	//append gameId, path only contains the folder here.
	path += SConfig::GetInstance().GetUniqueID();

	std::string name;
	for (int i = 1; File::Exists(name = StringFromFormat("%s-%d.png", path.c_str(), i)); ++i)
	{
		// TODO?
	}

	return name;
}

void SaveScreenShot()
{
	const bool bPaused = (GetState() == CORE_PAUSE);

	SetState(CORE_PAUSE);

	g_video_backend->Video_Screenshot(GenerateScreenshotName());

	if (!bPaused)
		SetState(CORE_RUN);
}

void SaveScreenShot(const std::string& name)
{
	const bool bPaused = (GetState() == CORE_PAUSE);

	SetState(CORE_PAUSE);

	std::string filePath = GenerateScreenshotFolderPath() + name + ".png";

	g_video_backend->Video_Screenshot(filePath);

	if (!bPaused)
	 	SetState(CORE_RUN);
}

void RequestRefreshInfo()
{
	s_request_refresh_info = true;
}

bool PauseAndLock(bool doLock, bool unpauseOnUnlock)
{
	if (!IsRunning())
		return true;

	// let's support recursive locking to simplify things on the caller's side,
	// and let's do it at this outer level in case the individual systems don't support it.
	if (doLock ? s_pause_and_lock_depth++ : --s_pause_and_lock_depth)
		return true;

	// first pause or unpause the CPU
	bool wasUnpaused = CPU::PauseAndLock(doLock, unpauseOnUnlock);
	ExpansionInterface::PauseAndLock(doLock, unpauseOnUnlock);

	// audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle).
	DSP::GetDSPEmulator()->PauseAndLock(doLock, unpauseOnUnlock);

	// video has to come after CPU, because CPU thread can wait for video thread (s_efbAccessRequested).
	g_video_backend->PauseAndLock(doLock, unpauseOnUnlock);

	return wasUnpaused;
}

// Apply Frame Limit and Display FPS info
// This should only be called from VI
void VideoThrottle()
{
	// Update info per second
	u32 ElapseTime = (u32)s_timer.GetTimeDifference();
	if ((ElapseTime >= 1000 && s_drawn_video.load() > 0) || s_request_refresh_info)
	{
		UpdateTitle();

		// Reset counter
		s_timer.Update();
		s_drawn_frame.store(0);
		s_drawn_video.store(0);
	}

	s_drawn_video++;
}

// Executed from GPU thread
// reports if a frame should be skipped or not
// depending on the framelimit set
bool ShouldSkipFrame(int skipped)
{
	const u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1)
		? (SConfig::GetInstance().m_Framelimit - 1) * 5
		: VideoInterface::TargetRefreshRate;
	const u32 frames = s_drawn_frame.load();
	const bool fps_slow = !(s_timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS);

	return fps_slow;
}

// --- Callbacks for backends / engine ---

// Should be called from GPU thread when a frame is drawn
void Callback_VideoCopiedToXFB(bool video_update)
{
	if (video_update)
		s_drawn_frame++;

	Movie::FrameUpdate();
}

void UpdateTitle()
{
	u32 ElapseTime = (u32)s_timer.GetTimeDifference();
	s_request_refresh_info = false;
	SConfig& _CoreParameter = SConfig::GetInstance();

	if (ElapseTime == 0)
		ElapseTime = 1;

	float FPS   = (float)(s_drawn_frame.load() * 1000.0 / ElapseTime);
	float VPS   = (float)(s_drawn_video.load() * 1000.0 / ElapseTime);
	float Speed = (float)(s_drawn_video.load() * (100 * 1000.0) / (VideoInterface::TargetRefreshRate * ElapseTime));

	// Settings are shown the same for both extended and summary info
	std::string SSettings = StringFromFormat("%s %s | %s | %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC",
		g_video_backend->GetDisplayName().c_str(), _CoreParameter.bDSPHLE ? "HLE" : "LLE");

	std::string SFPS;

	if (Movie::IsPlayingInput())
		SFPS = StringFromFormat("VI: %u/%u - Input: %u/%u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed);
	else if (Movie::IsRecordingInput())
		SFPS = StringFromFormat("VI: %u - Input: %u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed);
	else
	{
		SFPS = StringFromFormat("FPS: %.0f - VPS: %.0f - %.0f%%", FPS, VPS, Speed);
		if (SConfig::GetInstance().m_InterfaceExtendedFPSInfo)
		{
			// Use extended or summary information. The summary information does not print the ticks data,
			// that's more of a debugging interest, it can always be optional of course if someone is interested.
			static u64 ticks = 0;
			static u64 idleTicks = 0;
			u64 newTicks = CoreTiming::GetTicks();
			u64 newIdleTicks = CoreTiming::GetIdleTicks();

			u64 diff = (newTicks - ticks) / 1000000;
			u64 idleDiff = (newIdleTicks - idleTicks) / 1000000;

			ticks = newTicks;
			idleTicks = newIdleTicks;

			float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100;

			SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)",
					_CoreParameter.bSkipIdle ? "~" : "",
					(int)(diff),
					(int)(diff - idleDiff),
					(int)(idleDiff),
					SystemTimers::GetTicksPerSecond() / 1000000,
					_CoreParameter.bSkipIdle ? "~" : "",
					TicksPercentage);
		}
	}
	// This is our final "frame counter" string
	std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str());

	// Update the audio timestretcher with the current speed
	if (g_sound_stream)
	{
		CMixer* pMixer = g_sound_stream->GetMixer();
		pMixer->UpdateSpeed((float)Speed / 100);
	}

	Host_UpdateTitle(SMessage);
}

void Shutdown()
{
	// During shutdown DXGI expects us to handle some messages on the UI thread.
	// Therefore we can't immediately block and wait for the emu thread to shut
	// down, so we join the emu thread as late as possible when the UI has already
	// shut down.
	// For more info read "DirectX Graphics Infrastructure (DXGI): Best Practices"
	// on MSDN.
	if (s_emu_thread.joinable())
		s_emu_thread.join();
}

void SetOnStoppedCallback(StoppedCallbackFunc callback)
{
	s_on_stopped_callback = callback;
}

void UpdateWantDeterminism(bool initial)
{
	// For now, this value is not itself configurable.  Instead, individual
	// settings that depend on it, such as GPU determinism mode. should have
	// override options for testing,
	bool new_want_determinism =
		Movie::IsPlayingInput() ||
		Movie::IsRecordingInput() ||
		NetPlay::IsNetPlayRunning();
	if (new_want_determinism != g_want_determinism || initial)
	{
		WARN_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false");

		bool was_unpaused = Core::PauseAndLock(true);

		g_want_determinism = new_want_determinism;
		WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
		g_video_backend->UpdateWantDeterminism(new_want_determinism);
		// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use of FMA.
		JitInterface::ClearCache();
		Common::InitializeWiiRoot(g_want_determinism);

		Core::PauseAndLock(false, was_unpaused);
	}
}

} // Core
int main(int argc, char *argv[]){
    unsigned char stop, temp;
    unsigned char cmd;
    unsigned char arg1, arg2;
    char arg1buf[4], arg2buf[4];
    packet recievepkt;
    packet sendpkt;


    int fd_joy;			// File Descriptor for joystick
	int temp1;
	struct js_event e;
	temp1 = 0;               // Set first time stamp to zero
	fd_joy = open_joystick();   // /Initalize the joystick
   // printf("fd_joy: %d\n", fd_joy);


    strncpy(device, argv[1], DEVICE_MAX_SIZE);

    //set up serial port
    if((fd = configDevice()) < 2){
        perror("Error opening/configuring device\n");
        return -1;
        }

    menu();

    //set handler for close command
    signal(SIGUSR2, catchClose);
    signal(SIGTERM, catchClose);

    //create timer for ping
    pid = fork();
    if (pid < 0) printf("Error creating ping timer process\n");
    else if(pid == 0){
        //die if parent dies
        prctl(PR_SET_PDEATHSIG, SIGHUP);
        //set alarm for ping timeout
        signal(SIGALRM, sendPing);
        //prime Timeout reset handler
        signal(SIGUSR1, catchTimeoutReset);

        alarm(PING_INTERVAL);

        for(timeouts = 0; timeouts < PING_RETRIES;){
            //spin wheels. exit if ping timeout occurs
        }
        //kill parent process if ping timeout
        printf("Ping timeout. %d unanswered pings\n", timeouts);
        //tell parent to exit
        kill(getppid(), SIGKILL);
        return 1;
    }

    int readcount;
    char zeroflag;


    //main control loop
    for(stop = 0; stop <= 0;){

        //cmd = 'V';
        //arg1 = 0;
        //arg2 = 0;
        /*scanf("%c %s %s%c", &cmd, arg1buf, arg2buf, NULL);        //get command
        //strcpy(arg1buf, "Z");
        arg1 = (unsigned char)atoi(arg1buf);
        arg2 = (unsigned char)atoi(arg2buf);

        if(arg1 == 0){
            arg1 = 1;
        }
        if(arg2 == 0){
            arg2 = 1;
        }*/

        readcount = read (fd_joy, &e, sizeof(e));
		//printf("servo: %d\r", e.time);
        //printf("bytes read from joystick: %d\n", readcount);
		//if(e.time > temp1)
		{
            //printf("Bryan\n");
			if(e.type == 1 && e.value == 1){
                if(e.time > temp1){
                    button_press(e.number);
                    temp1 = e.time;
                    }
				}
			if(e.type == 2 /*&& e.value != 0*/){
			if(!zeroflag){
				axis_press(e.number, e.value);
				}
			//set zeroflag if event input zero
			//necessary to not keep resending packages
			//when in the zero (stopped) position
            zeroflag = (e.value == 0) ? 1 : 0;
            //printf("zeroflag %c\n", zeroflag);
            }
		}

        //while((temp = getchar()) != '\n' && temp != EOF);  //clear buffer
        //printf("CMD: %c ARG1: %c ARG2: %c\n", cmd, arg1, arg2);
        //stop = parseCommand(cmd, arg1, arg2);
    }
    close(fd);
    //tell child to exit
    kill(pid, SIGUSR2);
    t1.join();
    scanf("", NULL);
    return 0;
}
Ejemplo n.º 19
0
 ~LedgerCleanerImp () override
 {
     if (thread_.joinable())
         LogicError ("LedgerCleanerImp::onStop not called.");
 }
Ejemplo n.º 20
0
void Server::join() {
  serverThread_.join();
}
Ejemplo n.º 21
0
void CBackupDeleter::start()
{
	assert(!_thread.joinable());
	_thread= std::thread( &CBackupDeleter::run, this);
}
Ejemplo n.º 22
0
 void 
 GroupNodeImpl::join()    
 {    
   event_thread_->join();    
 }
Ejemplo n.º 23
0
void CMySourceParser::waitDone()
{
	if (_thread.joinable())
		_thread.join();
}
Ejemplo n.º 24
0
void join(std::thread &t)
{
  t.join();
}
	void detach() { internal_thread.detach(); }
Ejemplo n.º 26
0
Archivo: main.cpp Proyecto: CCJY/coliru
 ~concurrent() { q.push([=]{ done=true; }); thd.join(); }