// license:BSD-3-Clause
// copyright-holders:Maurizio Petrarota
/***************************************************************************
ui/datfile.cpp
UI DATs manager.
***************************************************************************/
#include "emu.h"
#include "drivenum.h"
#include "ui/moptions.h"
#include "ui/datfile.h"
#include "ui/utils.h"
//-------------------------------------------------
// TAGS
//-------------------------------------------------
static std::string DATAFILE_TAG("$");
static std::string TAG_BIO("$bio");
static std::string TAG_INFO("$info");
static std::string TAG_MAME("$mame");
static std::string TAG_COMMAND("$cmd");
static std::string TAG_END("$end");
static std::string TAG_DRIVER("$drv");
static std::string TAG_STORY("$story");
static std::string TAG_HISTORY_R("## REVISION:");
static std::string TAG_MAMEINFO_R("# MAMEINFO.DAT");
static std::string TAG_MESSINFO_R("# MESSINFO.DAT");
static std::string TAG_SYSINFO_R("# This file was generated on");
static std::string TAG_STORY_R("# version");
static std::string TAG_COMMAND_SEPARATOR("-----------------------------------------------");
static std::string TAG_GAMEINIT_R("# GAMEINIT.DAT");
//-------------------------------------------------
// Statics
//-------------------------------------------------
datfile_manager::dataindex datfile_manager::m_histidx;
datfile_manager::dataindex datfile_manager::m_mameidx;
datfile_manager::dataindex datfile_manager::m_messidx;
datfile_manager::dataindex datfile_manager::m_cmdidx;
datfile_manager::dataindex datfile_manager::m_sysidx;
datfile_manager::dataindex datfile_manager::m_storyidx;
datfile_manager::dataindex datfile_manager::m_ginitidx;
datfile_manager::drvindex datfile_manager::m_drvidx;
datfile_manager::drvindex datfile_manager::m_messdrvidx;
datfile_manager::drvindex datfile_manager::m_menuidx;
datfile_manager::swindex datfile_manager::m_swindex;
std::string datfile_manager::m_history_rev;
std::string datfile_manager::m_mame_rev;
std::string datfile_manager::m_mess_rev;
std::string datfile_manager::m_sysinfo_rev;
std::string datfile_manager::m_story_rev;
std::string datfile_manager::m_ginit_rev;
bool datfile_manager::first_run = true;
#define opendatsfile(f) if (parseopen(#f".dat")) { init_##f(); parseclose(); }
//-------------------------------------------------
// ctor
//-------------------------------------------------
datfile_manager::datfile_manager(running_machine &machine, ui_options &moptions)
: m_machine(machine)
, m_options(moptions)
{
if (m_options.enabled_dats() && first_run)
{
first_run = false;
opendatsfile(mameinfo);
opendatsfile(command);
opendatsfile(story);
opendatsfile(messinfo);
opendatsfile(sysinfo);
opendatsfile(history);
opendatsfile(gameinit);
}
}
//-------------------------------------------------
// initialize sysinfo.dat index
//-------------------------------------------------
void datfile_manager::init_sysinfo()
{
int swcount = 0;
auto count = index_datafile(m_sysidx, swcount, TAG_SYSINFO_R, m_sysinfo_rev, '.');
osd_printf_verbose("Sysinfo.dat games found = %i\n", count);
osd_printf_verbose("Rev = %s\n", m_sysinfo_rev.c_str());
}
//-------------------------------------------------
// initialize story.dat index
//-------------------------------------------------
void datfile_manager::init_story()
{
int swcount = 0;
auto count = index_datafile(m_storyidx, swcount, TAG_STORY_R, m_story_rev, 's');
osd_printf_verbose("Story.dat games found = %i\n", count);
}
//-------------------------------------------------
// initialize history.dat index
//-------------------------------------------------
void datfile_manager::init_history()
{
int swcount = 0;
auto count = index_datafile(m_histidx, swcount, TAG_HISTORY_R, m_history_rev, ' ');
osd_printf_verbose("History.dat systems found = %i\n", count);
osd_printf_verbose("History.dat software packages found = %i\n", swcount);
osd_printf_verbose("Rev = %s\n", m_history_rev.c_str());
}
//-------------------------------------------------
// initialize gameinit.dat index
//-------------------------------------------------
void datfile_manager::init_gameinit()
{
int swcount = 0;
drvindex tmp;
auto count = index_mame_mess_info(m_ginitidx, tmp, swcount);
osd_printf_verbose("Gameinit.dat games found = %i\n", count);
osd_printf_verbose("Rev = %s\n", m_ginit_rev.c_str());
}
//-------------------------------------------------
// initialize mameinfo.dat index
//-------------------------------------------------
void datfile_manager::init_mameinfo()
{
int drvcount = 0;
auto count = index_mame_mess_info(m_mameidx, m_drvidx, drvcount);
osd_printf_verbose("Mameinfo.dat games found = %i\n", count);
osd_printf_verbose("Mameinfo.dat drivers found = %d\n", drvcount);
osd_printf_verbose("Rev = %s\n", m_mame_rev.c_str());
}
//-------------------------------------------------
// initialize messinfo.dat index
//-------------------------------------------------
void datfile_manager::init_messinfo()
{
int drvcount = 0;
auto count = index_mame_mess_info(m_messidx, m_messdrvidx, drvcount);
osd_printf_verbose("Messinfo.dat games found = %i\n", count);
osd_printf_verbose("Messinfo.dat drivers found = %d\n", drvcount);
osd_printf_verbose("Rev = %s\n", m_mess_rev.c_str());
}
//-------------------------------------------------
// initialize command.dat index
//-------------------------------------------------
void datfile_manager::init_command()
{
int swcount = 0;
std::string tmp, tmp2;
auto count = index_datafile(m_cmdidx, swcount, tmp, tmp2, 'c');
osd_printf_verbose("Command.dat games found = %i\n", count);
}
bool datfile_manager::has_software(std::string &softlist, std::string &softname, std::string &parentname)
{
// Find software in software list index
if (m_swindex.find(softlist) == m_swindex.end())
return false;
m_itemsiter = m_swindex[softlist].find(softname);
if (m_itemsiter == m_swindex[softlist].end() && !parentname.empty())
m_itemsiter = m_swindex[softlist].find(parentname);
if (m_itemsiter == m_swindex[softlist].end())
return false;
return true;
}
//-------------------------------------------------
// load software info
//-------------------------------------------------
void datfile_manager::load_software_info(std::string &softlist, std::string &buffer, std::string &softname, std::string &parentname)
{
// Load history text
if (!m_swindex.empty() && parseopen("history.dat"))
{
// Find software in software list index
if (!has_software(softlist, softname, parentname))
return;
auto s_offset = m_itemsiter->second;
char rbuf[64 * 1024];
fseek(fp, s_offset, SEEK_SET);
std::string readbuf;
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
// end entry when a end tag is encountered
if (readbuf == TAG_END)
break;
// add this string to the buffer
buffer.append(readbuf).append("\n");
}
parseclose();
}
}
//-------------------------------------------------
// load_data_info
//-------------------------------------------------
void datfile_manager::load_data_info(const game_driver *drv, std::string &buffer, int type)
{
dataindex index_idx;
drvindex driver_idx;
std::string tag, filename;
switch (type)
{
case UI_HISTORY_LOAD:
filename = "history.dat";
tag = TAG_BIO;
index_idx = m_histidx;
break;
case UI_MAMEINFO_LOAD:
filename = "mameinfo.dat";
tag = TAG_MAME;
index_idx = m_mameidx;
driver_idx = m_drvidx;
break;
case UI_SYSINFO_LOAD:
filename = "sysinfo.dat";
tag = TAG_BIO;
index_idx = m_sysidx;
break;
case UI_MESSINFO_LOAD:
filename = "messinfo.dat";
tag = TAG_MAME;
index_idx = m_messidx;
driver_idx = m_messdrvidx;
break;
case UI_STORY_LOAD:
filename = "story.dat";
tag = TAG_STORY;
index_idx = m_storyidx;
break;
case UI_GINIT_LOAD:
filename = "gameinit.dat";
tag = TAG_MAME;
index_idx = m_ginitidx;
break;
}
if (parseopen(filename.c_str()))
{
load_data_text(drv, buffer, index_idx, tag);
// load driver info
if (!driver_idx.empty())
load_driver_text(drv, buffer, driver_idx, TAG_DRIVER);
// cleanup mameinfo and sysinfo double line spacing
if ((tag == TAG_MAME && type != UI_GINIT_LOAD) || type == UI_SYSINFO_LOAD)
strreplace(buffer, "\n\n", "\n");
parseclose();
}
}
//-------------------------------------------------
// load a game text into the buffer
//-------------------------------------------------
void datfile_manager::load_data_text(const game_driver *drv, std::string &buffer, dataindex &idx, std::string &tag)
{
auto itemsiter = idx.find(drv);
if (itemsiter == idx.end())
{
auto cloneof = driver_list::non_bios_clone(*drv);
if (cloneof == -1)
return;
else
{
auto c_drv = &driver_list::driver(cloneof);
itemsiter = idx.find(c_drv);
if (itemsiter == idx.end())
return;
}
}
auto s_offset = itemsiter->second;
fseek(fp, s_offset, SEEK_SET);
char rbuf[64 * 1024];
std::string readbuf;
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
// end entry when a end tag is encountered
if (readbuf == TAG_END)
break;
// continue if a specific tag is encountered
if (readbuf == tag)
continue;
// add this string to the buffer
buffer.append(readbuf).append("\n");
}
}
//-------------------------------------------------
// load a driver name and offset into an
// indexed array
//-------------------------------------------------
void datfile_manager::load_driver_text(const game_driver *drv, std::string &buffer, drvindex &idx, std::string &tag)
{
std::string s(core_filename_extract_base(drv->source_file));
auto index = idx.find(s);
// if driver not found, return
if (index == idx.end())
return;
buffer.append("\n--- DRIVER INFO ---\n").append("Driver: ").append(s).append("\n\n");
auto s_offset = index->second;
fseek(fp, s_offset, SEEK_SET);
char rbuf[64 * 1024];
std::string readbuf;
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
// end entry when a end tag is encountered
if (readbuf == TAG_END)
break;
// continue if a specific tag is encountered
if (readbuf == tag)
continue;
// add this string to the buffer
buffer.append(readbuf).append("\n");
}
}
//-------------------------------------------------
// load a game name and offset into an
// indexed array (mameinfo)
//-------------------------------------------------
int datfile_manager::index_mame_mess_info(dataindex &index, drvindex &index_drv, int &drvcount)
{
size_t foundtag;
auto t_mame = TAG_MAMEINFO_R.size();
auto t_mess = TAG_MESSINFO_R.size();
auto t_ginit = TAG_GAMEINIT_R.size();
auto t_info = TAG_INFO.size();
char rbuf[64 * 1024];
std::string readbuf, xid, name;
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
if (m_mame_rev.empty() && readbuf.compare(0, t_mame, TAG_MAMEINFO_R) == 0)
{
auto found = readbuf.find(" ", t_mame + 1);
m_mame_rev = readbuf.substr(t_mame + 1, found - t_mame);
}
else if (m_mess_rev.empty() && (foundtag = readbuf.find(TAG_MESSINFO_R)) != std::string::npos)
{
auto found = readbuf.find(" ", foundtag + t_mess + 1);
m_mess_rev = readbuf.substr(foundtag + t_mess + 1, found - t_mess - foundtag);
}
else if (m_ginit_rev.empty() && readbuf.compare(0, t_ginit, TAG_GAMEINIT_R) == 0)
{
auto found = readbuf.find(" ", t_ginit + 1);
m_ginit_rev = readbuf.substr(t_ginit + 1, found - t_ginit);
}
else if (readbuf.compare(0, t_info, TAG_INFO) == 0)
{
// TAG_INFO
fgets(rbuf, 64 * 1024, fp);
xid = chartrimcarriage(rbuf);
name = readbuf.substr(t_info + 1);
if (xid == TAG_MAME)
{
// validate driver
auto game_index = driver_list::find(name.c_str());
if (game_index != -1)
index.emplace(&driver_list::driver(game_index), ftell(fp));
}
else if (xid == TAG_DRIVER)
{
index_drv.emplace(name, ftell(fp));
drvcount++;
}
}
}
return index.size();
}
//-------------------------------------------------
// load a game name and offset into an
// indexed array
//-------------------------------------------------
int datfile_manager::index_datafile(dataindex &index, int &swcount, std::string &tag, std::string &str, char sep)
{
std::string readbuf;
auto tag_size = tag.size();
auto t_info = TAG_INFO.size();
auto t_bio = TAG_BIO.size();
char rbuf[64 * 1024];
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
if (!tag.empty())
if (str.empty() && readbuf.compare(0, tag_size, tag) == 0)
{
if (sep != 's')
{
auto found = readbuf.find(sep, tag_size + 1);
str = readbuf.substr(tag_size + 1, found - tag_size);
}
else
str = readbuf.substr(tag_size + 1);
}
if (readbuf.compare(0, t_info, TAG_INFO) == 0)
{
// search for game info
auto rd = readbuf.substr(t_info + 1);
std::vector<std::string> gamelist = tokenize(rd, ',');
for (auto & e : gamelist)
{
auto game_index = driver_list::find(e.c_str());
if (game_index != -1)
index.emplace(&driver_list::driver(game_index), ftell(fp));
}
}
else if (!readbuf.empty() && readbuf[0] == DATAFILE_TAG[0])
{
// search for software info
fgets(rbuf, 64 * 1024, fp);
std::string readbuf_2(chartrimcarriage(rbuf));
if (readbuf_2.compare(0, t_bio, TAG_BIO) == 0)
{
auto eq_sign = readbuf.find('=');
std::string s_list(readbuf.substr(1, eq_sign - 1));
std::string s_roms(readbuf.substr(eq_sign + 1));
std::vector<std::string> token_list = tokenize(s_list, ',');
std::vector<std::string> token_roms = tokenize(s_roms, ',');
for (auto & li : token_list)
for (auto & ro : token_roms)
m_swindex[li].emplace(ro, ftell(fp));
swcount++;
}
}
}
return index.size();
}
//---------------------------------------------------------
// parseopen - Open up file for reading
//---------------------------------------------------------
bool datfile_manager::parseopen(const char *filename)
{
emu_file file(m_options.history_path(), OPEN_FLAG_READ);
if (file.open(filename) != osd_file::error::NONE)
return false;
m_fullpath = file.fullpath();
file.close();
fp = fopen(m_fullpath.c_str(), "rb");
fgetc(fp);
fseek(fp, 0, SEEK_SET);
return true;
}
//-------------------------------------------------
// create the menu index
//-------------------------------------------------
void datfile_manager::index_menuidx(const game_driver *drv, dataindex &idx, drvindex &index)
{
dataindex::iterator itemsiter = idx.find(drv);
if (itemsiter == idx.end())
{
auto cloneof = driver_list::non_bios_clone(*drv);
if (cloneof == -1)
return;
else
{
auto c_drv = &driver_list::driver(cloneof);
if ((itemsiter = idx.find(c_drv)) == idx.end())
return;
}
}
// seek to correct point in datafile
auto s_offset = itemsiter->second;
fseek(fp, s_offset, SEEK_SET);
auto tinfo = TAG_INFO.size();
char rbuf[64 * 1024];
std::string readbuf;
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
if (!core_strnicmp(TAG_INFO.c_str(), readbuf.c_str(), tinfo))
break;
// TAG_COMMAND identifies the driver
if (readbuf == TAG_COMMAND)
{
fgets(rbuf, 64 * 1024, fp);
chartrimcarriage(rbuf);
index.emplace(rbuf, ftell(fp));
}
}
}
//-------------------------------------------------
// load command text into the buffer
//-------------------------------------------------
void datfile_manager::load_command_info(std::string &buffer, std::string &sel)
{
if (parseopen("command.dat"))
{
// open and seek to correct point in datafile
auto offset = m_menuidx.at(sel);
fseek(fp, offset, SEEK_SET);
char rbuf[64 * 1024];
std::string readbuf;
while (fgets(rbuf, 64 * 1024, fp) != nullptr)
{
readbuf = chartrimcarriage(rbuf);
// skip separator lines
if (readbuf == TAG_COMMAND_SEPARATOR)
continue;
// end entry when a tag is encountered
if (readbuf == TAG_END)
break;
// add this string to the buffer
buffer.append(readbuf).append("\n");;
}
parseclose();
}
}
//-------------------------------------------------
// load submenu item for command.dat
//-------------------------------------------------
void datfile_manager::command_sub_menu(const game_driver *drv, std::vector<std::string> &menuitems)
{
if (parseopen("command.dat"))
{
m_menuidx.clear();
index_menuidx(drv, m_cmdidx, m_menuidx);
menuitems.reserve(m_menuidx.size());
for (auto & elem : m_menuidx)
menuitems.push_back(elem.first);
parseclose();
}
}