<commit_before>#include "include/consul_admin.h"

//Global writedata instantiation to store HTTP Callbacks
std::string writedata;

//----------------------------HTTP Callbacks----------------------------------//

//This is the callback that gets called when we recieve the response to the
//Get Curl Request
size_t writeCallback(char * buf, size_t size, size_t nmemb, void* up)
{

  logging->debug("CONSUL: HTTP Query Callback Triggered");

//Put the response into a string
for (int c = 0; c<size*nmemb; c++)
{
	writedata.push_back(buf[c]);
}

return size*nmemb;
}

//--------------------------------Service-------------------------------------//

Service::Service()
{
  id="";
  name="";
  address="";
  port="";
}

Service::Service(std::string new_id, std::string new_name)
{
  id=new_id;
  name=new_name;
  address="";
  port="";
}

Service::Service(std::string new_id, std::string new_name, std::string new_address, std::string new_port)
{
  id=new_id;
  name=new_name;
  address=new_address;
  port=new_port;
}

Service::Service(std::string new_id, std::string new_name, std::string new_address, std::string new_port, std::vector<std::string> new_tags)
{
  id=new_id;
  name=new_name;
  address=new_address;
  port=new_port;
  tags=new_tags;
}

std::string Service::to_json()
{
  logging->debug("CONSUL: Service to JSON method Called:");
  logging->debug(id);

  std::string id_key = "ID";
  std::string name_key = "Name";
  std::string tags_key = "Tags";
  std::string addr_key = "Address";
  std::string port_key = "Port";

  std::string json_str = "{\"" + id_key + "\": \"" + id + "\", \"" +
    name_key + "\": " + name + "\", \"" +
      tags_key + "\": [\"" + tags[0] + "\"";

  for (int i=1; i<num_tags(); i++) {
    json_str = json_str + ", \"" + tags[i] + "\"";
  }

  json_str = json_str + "], \"" + addr_key + "\": \"" + address + "\"" +
    ", \"" + port_key + "\": " + port + "}";

  logging->debug(json_str);

  return json_str;

  //Initialize the string buffer and writer
  // rapidjson::StringBuffer s;
  // rapidjson::Writer<rapidjson::StringBuffer> writer(s);
  //
  // //Start writing the object
  //
  // writer.StartObject();
  //
  // writer.Key("ID");
  // std::string id = id;
  // writer.String( id.c_str(), (rapidjson::SizeType)id.length() );
  //
  // writer.Key("Name");
  // std::string name = name;
  // writer.String( name.c_str(), (rapidjson::SizeType)name.length() );
  //
  // writer.Key("Tags");
  // std::string tag;
  // writer.StartArray();
  // for (int i=0; i<num_tags(); i++) {
  //   tag = tags[i];
  //   writer.String( tag.c_str(), (rapidjson::SizeType)tag.length() );
  // }
  // writer.EndArray();
  //
  // writer.Key("Address");
  // std::string addr = address;
  // writer.String( addr.c_str(), (rapidjson::SizeType)addr.length() );
  //
  // writer.Key("Port");
  // std::string p = port;
  // writer.String( p.c_str(), (rapidjson::SizeType)p.length() );
  //
  // writer.EndObject();
  //
  // //The Stringbuffer now contains a json message
  // //of the object
	// const char* ret_val = s.GetString();
	// std::string ret_string (ret_val);
  // logging->debug(ret_string);
  // return ret_string;

}

//------------------------Consul Administrator--------------------------------//

//Put together a url query segment with the consul address provided at initialization
std::string ConsulAdmin::build_url(std::string request_url_segment)
{
  std::string url = consul_addr;
  url = url.append(request_url_segment);
  return url;
}

//Post a query to consul
std::string ConsulAdmin::query(std::string query_url)
{
  logging->info("CONSUL: Firing Query");
  logging->debug(query_url);
  //Clear the string that will hold the response data.
  writedata.clear();
  //Get the URL
  std::string url_string = build_url(query_url);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  curl_easy_setopt(ha->get_instance(), CURLOPT_WRITEFUNCTION, &writeCallback);

  //Send the HTTP Request
  bool success = ha->get(url, timeout);
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Query Successful");
    return writedata;
  }
  else
  {
    logging->debug("CONSUL: Query Failed");
    return "";
  }
}

//-------------------Service Registry Functions-------------------------------//

bool ConsulAdmin::register_service(Service& s)
{
  logging->debug("CONSUL: Registering Service");
  //Get the URL
  std::string url_string = build_url("/v1/agent/service/register");
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Get the message body
  std::string body_str = s.to_json();
  const char * body_cstr = body_str.c_str();
  char *body = new char[body_str.length() + 1];
  strcpy(body, body_cstr);

  //Send the HTTP Request
  bool success = ha->put(url, body, timeout);
  delete body;
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Registration Successful");
  }
  else
  {
    logging->debug("CONSUL: Registration Unsuccessful");
  }
  return success;
}

bool ConsulAdmin::deregister_service(Service& s)
{
  //Get the URL
  std::string url_string = "/v1/agent/service/deregister/";
  url_string = url_string.append(s.get_id());
  url_string = build_url(url_string);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Send the HTTP Request
  bool success = ha->put(url, "", timeout);
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Deregistration Successful");
  }
  else
  {
    logging->debug("CONSUL: Deregistration Unsuccessful");
  }
  return success;
}

//----------------------------Basic Queries-----------------------------------//

std::string services()
{
  std::string url = "/v1/agent/services";
  return query(url);
}

//--------------Configuration Key-Value Storage Functions---------------------//

bool ConsulAdmin::set_config_value(std::string key, std::string val)
{
  logging->info("CONSUL: Setting Configuration Value");
  logging->debug(key);
  logging->debug(val);
  //Get the URL
  std::string url_string = build_url("/v1/kv/");
  url_string = url_string.append(key);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Get the message body
  const char * body_cstr = val.c_str();
  char *body = new char[val.length() + 1];
  strcpy(body, body_cstr);

  //Send the HTTP Request
  bool success = ha->put(url, body, timeout);
  delete body;
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Config Value Set Successful");
  }
  else
  {
    logging->debug("CONSUL: Config Value Set Unsuccessful");
  }
  return success;
}

std::string ConsulAdmin::get_config_value(std::string key)
{
  std::string url = "/v1/kv/";
  url = url.append(key);
  return query(url);
}

bool ConsulAdmin::del_config_value(std::string key)
{
  logging->info("CONSUL: Deleting Configuration Value");
  logging->debug(key);
  //Get the URL
  std::string url_string = build_url("/v1/kv/");
  url_string = url_string.append(key);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Send the HTTP Request
  bool success = ha->del(url, timeout);
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Config Value Delete Successful");
  }
  else
  {
    logging->debug("CONSUL: Config Value Delete Unsuccessful");
  }
  return success;
}

//Appear to be depricated?

std::string ConsulAdmin::datacenters()
{
  return query("/v1/catalog/datacenters");
}

std::string ConsulAdmin::nodes_dc(std::string data_center)
{
  std::string url = "/v1/catalog/nodes";
  if (!data_center.empty())
  {
    url = url.append("?dc=");
    url = url.append(data_center);
  }
  return query(url);
}

std::string ConsulAdmin::services_dc(std::string data_center)
{
  std::string url = "/v1/catalog/services";
  if (!data_center.empty())
  {
    url = url.append("?dc=");
    url = url.append(data_center);
  }
  return query(url);
}

std::string ConsulAdmin::nodes_service(std::string service)
{
  std::string url = "/v1/catalog/service/";
  url = url.append(service);
  return query(url);
}

std::string ConsulAdmin::services_node(std::string node, std::string data_center)
{
  std::string url = "/v1/catalog/node/";
  url = url.append(node);
  if (!data_center.empty())
  {
    url = url.append("?dc=");
    url = url.append(data_center);
  }
  return query(url);
}
<commit_msg>Consul Fix<commit_after>#include "include/consul_admin.h"

//Global writedata instantiation to store HTTP Callbacks
std::string writedata;

//----------------------------HTTP Callbacks----------------------------------//

//This is the callback that gets called when we recieve the response to the
//Get Curl Request
size_t writeCallback(char * buf, size_t size, size_t nmemb, void* up)
{

  logging->debug("CONSUL: HTTP Query Callback Triggered");

//Put the response into a string
for (int c = 0; c<size*nmemb; c++)
{
	writedata.push_back(buf[c]);
}

return size*nmemb;
}

//--------------------------------Service-------------------------------------//

Service::Service()
{
  id="";
  name="";
  address="";
  port="";
}

Service::Service(std::string new_id, std::string new_name)
{
  id=new_id;
  name=new_name;
  address="";
  port="";
}

Service::Service(std::string new_id, std::string new_name, std::string new_address, std::string new_port)
{
  id=new_id;
  name=new_name;
  address=new_address;
  port=new_port;
}

Service::Service(std::string new_id, std::string new_name, std::string new_address, std::string new_port, std::vector<std::string> new_tags)
{
  id=new_id;
  name=new_name;
  address=new_address;
  port=new_port;
  tags=new_tags;
}

std::string Service::to_json()
{
  logging->debug("CONSUL: Service to JSON method Called:");
  logging->debug(id);

  std::string id_key = "ID";
  std::string name_key = "Name";
  std::string tags_key = "Tags";
  std::string addr_key = "Address";
  std::string port_key = "Port";

  std::string json_str = "{\"" + id_key + "\": \"" + id + "\", \"" +
    name_key + "\": " + name + "\", \"" +
      tags_key + "\": [\"" + tags[0] + "\"";

  for (int i=1; i<num_tags(); i++) {
    json_str = json_str + ", \"" + tags[i] + "\"";
  }

  json_str = json_str + "], \"" + addr_key + "\": \"" + address + "\"" +
    ", \"" + port_key + "\": " + port + "}";

  logging->debug(json_str);

  return json_str;

  //Initialize the string buffer and writer
  // rapidjson::StringBuffer s;
  // rapidjson::Writer<rapidjson::StringBuffer> writer(s);
  //
  // //Start writing the object
  //
  // writer.StartObject();
  //
  // writer.Key("ID");
  // std::string id = id;
  // writer.String( id.c_str(), (rapidjson::SizeType)id.length() );
  //
  // writer.Key("Name");
  // std::string name = name;
  // writer.String( name.c_str(), (rapidjson::SizeType)name.length() );
  //
  // writer.Key("Tags");
  // std::string tag;
  // writer.StartArray();
  // for (int i=0; i<num_tags(); i++) {
  //   tag = tags[i];
  //   writer.String( tag.c_str(), (rapidjson::SizeType)tag.length() );
  // }
  // writer.EndArray();
  //
  // writer.Key("Address");
  // std::string addr = address;
  // writer.String( addr.c_str(), (rapidjson::SizeType)addr.length() );
  //
  // writer.Key("Port");
  // std::string p = port;
  // writer.String( p.c_str(), (rapidjson::SizeType)p.length() );
  //
  // writer.EndObject();
  //
  // //The Stringbuffer now contains a json message
  // //of the object
	// const char* ret_val = s.GetString();
	// std::string ret_string (ret_val);
  // logging->debug(ret_string);
  // return ret_string;

}

//------------------------Consul Administrator--------------------------------//

//Put together a url query segment with the consul address provided at initialization
std::string ConsulAdmin::build_url(std::string request_url_segment)
{
  std::string url = consul_addr;
  url = url.append(request_url_segment);
  return url;
}

//Post a query to consul
std::string ConsulAdmin::query(std::string query_url)
{
  logging->info("CONSUL: Firing Query");
  logging->debug(query_url);
  //Clear the string that will hold the response data.
  writedata.clear();
  //Get the URL
  std::string url_string = build_url(query_url);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  curl_easy_setopt(ha->get_instance(), CURLOPT_WRITEFUNCTION, &writeCallback);

  //Send the HTTP Request
  bool success = ha->get(url, timeout);
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Query Successful");
    return writedata;
  }
  else
  {
    logging->debug("CONSUL: Query Failed");
    return "";
  }
}

//-------------------Service Registry Functions-------------------------------//

bool ConsulAdmin::register_service(Service& s)
{
  logging->debug("CONSUL: Registering Service");
  //Get the URL
  std::string url_string = build_url("/v1/agent/service/register");
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Get the message body
  std::string body_str = s.to_json();
  const char * body_cstr = body_str.c_str();
  char *body = new char[body_str.length() + 1];
  strcpy(body, body_cstr);

  //Send the HTTP Request
  bool success = ha->put(url, body, timeout);
  delete body;
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Registration Successful");
  }
  else
  {
    logging->debug("CONSUL: Registration Unsuccessful");
  }
  return success;
}

bool ConsulAdmin::deregister_service(Service& s)
{
  //Get the URL
  std::string url_string = "/v1/agent/service/deregister/";
  url_string = url_string.append(s.get_id());
  url_string = build_url(url_string);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Send the HTTP Request
  bool success = ha->put(url, "", timeout);
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Deregistration Successful");
  }
  else
  {
    logging->debug("CONSUL: Deregistration Unsuccessful");
  }
  return success;
}

//----------------------------Basic Queries-----------------------------------//

std::string services()
{
  std::string url = "/v1/agent/services";
  std::string result = query(url);
  return result;
}

//--------------Configuration Key-Value Storage Functions---------------------//

bool ConsulAdmin::set_config_value(std::string key, std::string val)
{
  logging->info("CONSUL: Setting Configuration Value");
  logging->debug(key);
  logging->debug(val);
  //Get the URL
  std::string url_string = build_url("/v1/kv/");
  url_string = url_string.append(key);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Get the message body
  const char * body_cstr = val.c_str();
  char *body = new char[val.length() + 1];
  strcpy(body, body_cstr);

  //Send the HTTP Request
  bool success = ha->put(url, body, timeout);
  delete body;
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Config Value Set Successful");
  }
  else
  {
    logging->debug("CONSUL: Config Value Set Unsuccessful");
  }
  return success;
}

std::string ConsulAdmin::get_config_value(std::string key)
{
  std::string url = "/v1/kv/";
  url = url.append(key);
  return query(url);
}

bool ConsulAdmin::del_config_value(std::string key)
{
  logging->info("CONSUL: Deleting Configuration Value");
  logging->debug(key);
  //Get the URL
  std::string url_string = build_url("/v1/kv/");
  url_string = url_string.append(key);
  const char * url_cstr = url_string.c_str();
  char *url = new char[url_string.length() + 1];
  strcpy(url, url_cstr);

  //Send the HTTP Request
  bool success = ha->del(url, timeout);
  delete url;
  if (success)
  {
    logging->debug("CONSUL: Config Value Delete Successful");
  }
  else
  {
    logging->debug("CONSUL: Config Value Delete Unsuccessful");
  }
  return success;
}

//Appear to be depricated?

std::string ConsulAdmin::datacenters()
{
  return query("/v1/catalog/datacenters");
}

std::string ConsulAdmin::nodes_dc(std::string data_center)
{
  std::string url = "/v1/catalog/nodes";
  if (!data_center.empty())
  {
    url = url.append("?dc=");
    url = url.append(data_center);
  }
  return query(url);
}

std::string ConsulAdmin::services_dc(std::string data_center)
{
  std::string url = "/v1/catalog/services";
  if (!data_center.empty())
  {
    url = url.append("?dc=");
    url = url.append(data_center);
  }
  return query(url);
}

std::string ConsulAdmin::nodes_service(std::string service)
{
  std::string url = "/v1/catalog/service/";
  url = url.append(service);
  return query(url);
}

std::string ConsulAdmin::services_node(std::string node, std::string data_center)
{
  std::string url = "/v1/catalog/node/";
  url = url.append(node);
  if (!data_center.empty())
  {
    url = url.append("?dc=");
    url = url.append(data_center);
  }
  return query(url);
}
<|endoftext|>


<commit_before>#include <qt/mintingtablemodel.h>
#include <qt/mintingfilterproxy.h>

#include <kernelrecord.h>
#include <qt/transactiondesc.h>

//#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <qt/guiconstants.h>
#include <qt/bitcoinunits.h>
#include <qt/optionsmodel.h>
#include <qt/addresstablemodel.h>

#include <util.h>
#include <wallet/wallet.h>
#include <validation.h>

#include <ui_interface.h>

#include <QColor>
#include <QTimer>

// Amount column is right-aligned it contains numbers
static int column_alignments[] = {
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter
    };

// Comparison operator for sort/binary search of model tx list
struct TxLessThan
{
    bool operator()(const KernelRecord &a, const KernelRecord &b) const
    {
        return a.hash < b.hash;
    }
    bool operator()(const KernelRecord &a, const uint256 &b) const
    {
        return a.hash < b;
    }
    bool operator()(const uint256 &a, const KernelRecord &b) const
    {
        return a < b.hash;
    }
};

// Private implementation
class MintingTablePriv
{
public:
    MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
            wallet(wallet),
            parent(parent)
    {
    }
    CWallet *wallet;
    MintingTableModel *parent;

    /* Local cache of wallet.
     * As it is in the same order as the CWallet, by definition
     * this is sorted by sha256.
     */
    QList<KernelRecord> cachedWallet;

    /* Query entire wallet anew from core.
     */
    void refreshWallet()
    {
        LogPrintf("refreshWallet\n");
        cachedWallet.clear();
        {
            // cs_main lock was added because GetDepthInMainChain requires it
            LOCK2(cs_main, wallet->cs_wallet);
            for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
            {
                std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
                if(KernelRecord::showTransaction(it->second))
                    for(const KernelRecord& kr : txList) {
                        if(!kr.spent) {
                            cachedWallet.append(kr);
                        }
                    }
            }
        }
    }

    /* Update our model of the wallet incrementally, to synchronize our model of the wallet
       with that of the core.

       Call with transaction that was added, removed or changed.
     */
    void updateWallet(const uint256 &hash, int status)
    {
        LogPrintf("minting updateWallet %s %i\n", hash.ToString(), status);
        {
            LOCK2(cs_main, wallet->cs_wallet);

            // Find transaction in wallet
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
            bool inWallet = mi != wallet->mapWallet.end();

            // Find bounds of this transaction in model
            QList<KernelRecord>::iterator lower = qLowerBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            QList<KernelRecord>::iterator upper = qUpperBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            int lowerIndex = (lower - cachedWallet.begin());
            int upperIndex = (upper - cachedWallet.begin());
            bool inModel = (lower != upper);

            // Determine whether to show transaction or not
            bool showTransaction = (inWallet && KernelRecord::showTransaction(mi->second));

            if(status == CT_UPDATED)
            {
                if(showTransaction && !inModel)
                    status = CT_NEW; /* Not in model, but want to show, treat as new */
                if(!showTransaction && inModel)
                    status = CT_DELETED; /* In model, but want to hide, treat as deleted */
            }

            LogPrintf("   inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
                     inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);

            switch(status)
            {
            case CT_NEW:
                if(inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
                    break;
                }
                if(!inWallet)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
                    break;
                }
                if(showTransaction)
                {
                    // Added -- insert at the right position
                    std::vector<KernelRecord> toInsert =
                            KernelRecord::decomposeOutput(wallet, mi->second);
                    if(toInsert.size() != 0) /* only if something to insert */
                    {
                        parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
                        int insert_idx = lowerIndex;
                        for (const KernelRecord &rec : toInsert)
                        {
                            if(!rec.spent)
                            {
                                cachedWallet.insert(insert_idx, rec);
                                insert_idx += 1;
                            }
                        }
                        parent->endInsertRows();
                    }
                }
                break;
            case CT_DELETED:
                if(!inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
                    break;
                }
                // Removed -- remove entire transaction from table
                parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
                cachedWallet.erase(lower, upper);
                parent->endRemoveRows();
                break;
            case CT_UPDATED:
                // Updated -- remove spent coins from table
                std::vector<KernelRecord> toCheck = KernelRecord::decomposeOutput(wallet, mi->second);
                if(!toCheck.empty())
                {
                    for(const KernelRecord &rec : toCheck)
                    {
                        if(rec.spent)
                        {
                            for(int i = lowerIndex; i < upperIndex; i++)
                            {
                                KernelRecord cachedRec = cachedWallet.at(i);
                                if((rec.address == cachedRec.address)
                                   && (rec.nValue == cachedRec.nValue)
                                   && (rec.idx == cachedRec.idx))
                                {
                                    parent->beginRemoveRows(QModelIndex(), i, i);
                                    cachedWallet.removeAt(i);
                                    parent->endRemoveRows();
                                    break;
                                }
                            }
                        }
                    }
                }
                break;
            }
        }
    }

    int size()
    {
        return cachedWallet.size();
    }

    KernelRecord *index(int idx)
    {
        if(idx >= 0 && idx < cachedWallet.size())
        {
            KernelRecord *rec = &cachedWallet[idx];
            return rec;
        }
        else
        {
            return 0;
        }
    }

    QString describe(KernelRecord *rec)
    {
        {
            LOCK(wallet->cs_wallet);
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
            if(mi != wallet->mapWallet.end())
            {
                return TransactionDesc::toHTML(wallet, mi->second, nullptr, BitcoinUnits::BTC);  //ppcTODO - fix the last 2 parameters
            }
        }
        return QString("");
    }

};

struct TransactionNotification
{
public:
    TransactionNotification() {}
    TransactionNotification(uint256 _hash, ChangeType _status):
        hash(_hash), status(_status) {}

    void invoke(QObject *ttm)
    {
        QString strHash = QString::fromStdString(hash.GetHex());
        QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
                                  Q_ARG(QString, strHash),
                                  Q_ARG(int, status));
    }
private:
    uint256 hash;
    ChangeType status;
};

static void NotifyTransactionChanged(MintingTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status)
{
    TransactionNotification notification(hash, status);
    notification.invoke(ttm);
}

MintingTableModel::MintingTableModel(CWallet* wallet, WalletModel *parent) :
        QAbstractTableModel(parent),
        wallet(wallet),
        walletModel(parent),
        mintingInterval(10),
        priv(new MintingTablePriv(wallet, this)),
        cachedNumBlocks(0)
{
    columns << tr("Transaction") <<  tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability");

    priv->refreshWallet();

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateAge()));
    timer->start(MODEL_UPDATE_DELAY);

    connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
    wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
}

MintingTableModel::~MintingTableModel()
{
    wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
    delete priv;
}

void MintingTableModel::updateTransaction(const QString &hash, int status)
{
    uint256 updated;
    updated.SetHex(hash.toStdString());

    priv->updateWallet(updated, status);
    mintingProxyModel->invalidate(); // Force deletion of empty rows
}

void MintingTableModel::updateAge()
{
    Q_EMIT dataChanged(index(0, Age), index(priv->size()-1, Age));
    Q_EMIT dataChanged(index(0, CoinDay), index(priv->size()-1, CoinDay));
    Q_EMIT dataChanged(index(0, MintProbability), index(priv->size()-1, MintProbability));
}

void MintingTableModel::setMintingProxyModel(MintingFilterProxy *mintingProxy)
{
    mintingProxyModel = mintingProxy;
}

int MintingTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MintingTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MintingTableModel::data(const QModelIndex &index, int role) const
{
    const Consensus::Params& params = Params().GetConsensus();
    if(!index.isValid())
        return QVariant();
    KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());

    switch(role)
    {
      case Qt::DisplayRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return formatTxAge(rec);
        case Balance:
            return formatTxBalance(rec);
        case CoinDay:
            return formatTxCoinDay(rec);
        case MintProbability:
            return formatDayToMint(rec);
        }
        break;
      case Qt::TextAlignmentRole:
        return column_alignments[index.column()];
        break;
      case Qt::ToolTipRole:
        switch(index.column())
        {
        case MintProbability:
            int interval = this->mintingInterval;
            QString unit = tr("minutes");

            int hours = interval / 60;
            int days = hours  / 24;

            if(hours > 1) {
                interval = hours;
                unit = tr("hours");
            }
            if(days > 1) {
                interval = days;
                unit = tr("days");
            }

            QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
            return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
        }
        break;
      case Qt::EditRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return qint64(rec->getAge());
        case CoinDay:
            return qint64(rec->coinAge);
        case Balance:
            return qint64(rec->nValue);
        case MintProbability:
            return getDayToMint(rec);
        }
        break;
      case Qt::BackgroundColorRole:
        int minAge = params.nStakeMinAge / 60 / 60 / 24;
        int maxAge = params.nStakeMaxAge / 60 / 60 / 24;
        if(rec->getAge() < minAge)
        {
            return COLOR_MINT_YOUNG;
        }
        else if (rec->getAge() >= minAge && rec->getAge() < maxAge)
        {
            return COLOR_MINT_MATURE;
        }
        else
        {
            return COLOR_MINT_OLD;
        }
        break;

    }
    return QVariant();
}

void MintingTableModel::setMintingInterval(int interval)
{
    mintingInterval = interval;
}

QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
{
    QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
    QString description;
    if(!label.isEmpty())
    {
        description += label + QString(" ");
    }
    if(label.isEmpty() || tooltip)
    {
        description += QString(" (") + QString::fromStdString(address) + QString(")");
    }
    return description;
}

double MintingTableModel::getDayToMint(KernelRecord *wtx) const
{
    const CBlockIndex *p = GetLastBlockIndex(chainActive.Tip(), true);
    double difficulty = p->GetBlockDifficulty();

    double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
    prob = prob * 100;
    return prob;
}

QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
{
    double prob = getDayToMint(wtx);
    return QString::number(prob, 'f', 6) + "%";
}

QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
{
    return QString::fromStdString(wtx->address);
}

QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
{
    return QString::fromStdString(wtx->hash.ToString());
}

QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
{
    return QString::number(wtx->coinAge);
}

QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
{
    int64_t nAge = wtx->getAge();
    return QString::number(nAge);
}

QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
{
    return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
}

QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
        else if (role == Qt::TextAlignmentRole)
        {
            return column_alignments[section];
        } else if (role == Qt::ToolTipRole)
        {
            switch(section)
            {
            case Address:
                return tr("Destination address of the output.");
            case TxHash:
                return tr("Original transaction id.");
            case Age:
                return tr("Age of the transaction in days.");
            case Balance:
                return tr("Balance of the output.");
            case CoinDay:
                return tr("Coin age in the output.");
            case MintProbability:
                return tr("Chance to mint a block within given time interval.");
            }
        }
    }
    return QVariant();
}

QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    KernelRecord *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MintingTableModel::updateDisplayUnit()
{
    // emit dataChanged to update Balance column with the current unit
    Q_EMIT dataChanged(index(0, Balance), index(priv->size()-1, Balance));
}
<commit_msg>fix conflicting struct name<commit_after>#include <qt/mintingtablemodel.h>
#include <qt/mintingfilterproxy.h>

#include <kernelrecord.h>
#include <qt/transactiondesc.h>

//#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <qt/guiconstants.h>
#include <qt/bitcoinunits.h>
#include <qt/optionsmodel.h>
#include <qt/addresstablemodel.h>

#include <util.h>
#include <wallet/wallet.h>
#include <validation.h>

#include <ui_interface.h>

#include <QColor>
#include <QTimer>

// Amount column is right-aligned it contains numbers
static int column_alignments[] = {
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter
    };

// Comparison operator for sort/binary search of model tx list
struct TxLessThan
{
    bool operator()(const KernelRecord &a, const KernelRecord &b) const
    {
        return a.hash < b.hash;
    }
    bool operator()(const KernelRecord &a, const uint256 &b) const
    {
        return a.hash < b;
    }
    bool operator()(const uint256 &a, const KernelRecord &b) const
    {
        return a < b.hash;
    }
};

// Private implementation
class MintingTablePriv
{
public:
    MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
            wallet(wallet),
            parent(parent)
    {
    }
    CWallet *wallet;
    MintingTableModel *parent;

    /* Local cache of wallet.
     * As it is in the same order as the CWallet, by definition
     * this is sorted by sha256.
     */
    QList<KernelRecord> cachedWallet;

    /* Query entire wallet anew from core.
     */
    void refreshWallet()
    {
        LogPrintf("refreshWallet\n");
        cachedWallet.clear();
        {
            // cs_main lock was added because GetDepthInMainChain requires it
            LOCK2(cs_main, wallet->cs_wallet);
            for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
            {
                std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
                if(KernelRecord::showTransaction(it->second))
                    for(const KernelRecord& kr : txList) {
                        if(!kr.spent) {
                            cachedWallet.append(kr);
                        }
                    }
            }
        }
    }

    /* Update our model of the wallet incrementally, to synchronize our model of the wallet
       with that of the core.

       Call with transaction that was added, removed or changed.
     */
    void updateWallet(const uint256 &hash, int status)
    {
        LogPrintf("minting updateWallet %s %i\n", hash.ToString(), status);
        {
            LOCK2(cs_main, wallet->cs_wallet);

            // Find transaction in wallet
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
            bool inWallet = mi != wallet->mapWallet.end();

            // Find bounds of this transaction in model
            QList<KernelRecord>::iterator lower = qLowerBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            QList<KernelRecord>::iterator upper = qUpperBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            int lowerIndex = (lower - cachedWallet.begin());
            int upperIndex = (upper - cachedWallet.begin());
            bool inModel = (lower != upper);

            // Determine whether to show transaction or not
            bool showTransaction = (inWallet && KernelRecord::showTransaction(mi->second));

            if(status == CT_UPDATED)
            {
                if(showTransaction && !inModel)
                    status = CT_NEW; /* Not in model, but want to show, treat as new */
                if(!showTransaction && inModel)
                    status = CT_DELETED; /* In model, but want to hide, treat as deleted */
            }

            LogPrintf("   inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
                     inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);

            switch(status)
            {
            case CT_NEW:
                if(inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
                    break;
                }
                if(!inWallet)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
                    break;
                }
                if(showTransaction)
                {
                    // Added -- insert at the right position
                    std::vector<KernelRecord> toInsert =
                            KernelRecord::decomposeOutput(wallet, mi->second);
                    if(toInsert.size() != 0) /* only if something to insert */
                    {
                        parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
                        int insert_idx = lowerIndex;
                        for (const KernelRecord &rec : toInsert)
                        {
                            if(!rec.spent)
                            {
                                cachedWallet.insert(insert_idx, rec);
                                insert_idx += 1;
                            }
                        }
                        parent->endInsertRows();
                    }
                }
                break;
            case CT_DELETED:
                if(!inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
                    break;
                }
                // Removed -- remove entire transaction from table
                parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
                cachedWallet.erase(lower, upper);
                parent->endRemoveRows();
                break;
            case CT_UPDATED:
                // Updated -- remove spent coins from table
                std::vector<KernelRecord> toCheck = KernelRecord::decomposeOutput(wallet, mi->second);
                if(!toCheck.empty())
                {
                    for(const KernelRecord &rec : toCheck)
                    {
                        if(rec.spent)
                        {
                            for(int i = lowerIndex; i < upperIndex; i++)
                            {
                                KernelRecord cachedRec = cachedWallet.at(i);
                                if((rec.address == cachedRec.address)
                                   && (rec.nValue == cachedRec.nValue)
                                   && (rec.idx == cachedRec.idx))
                                {
                                    parent->beginRemoveRows(QModelIndex(), i, i);
                                    cachedWallet.removeAt(i);
                                    parent->endRemoveRows();
                                    break;
                                }
                            }
                        }
                    }
                }
                break;
            }
        }
    }

    int size()
    {
        return cachedWallet.size();
    }

    KernelRecord *index(int idx)
    {
        if(idx >= 0 && idx < cachedWallet.size())
        {
            KernelRecord *rec = &cachedWallet[idx];
            return rec;
        }
        else
        {
            return 0;
        }
    }

    QString describe(KernelRecord *rec)
    {
        {
            LOCK(wallet->cs_wallet);
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
            if(mi != wallet->mapWallet.end())
            {
                return TransactionDesc::toHTML(wallet, mi->second, nullptr, BitcoinUnits::BTC);  //ppcTODO - fix the last 2 parameters
            }
        }
        return QString("");
    }

};

struct TransactionNotification2
{
public:
    TransactionNotification2() {}
    TransactionNotification2(uint256 _hash, ChangeType _status):
        hash(_hash), status(_status) {}

    void invoke(QObject *ttm)
    {
        QString strHash = QString::fromStdString(hash.GetHex());
        QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
                                  Q_ARG(QString, strHash),
                                  Q_ARG(int, status));
    }
private:
    uint256 hash;
    ChangeType status;
};

static void NotifyTransactionChanged(MintingTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status)
{
    TransactionNotification2 notification(hash, status);
    notification.invoke(ttm);
}

MintingTableModel::MintingTableModel(CWallet* wallet, WalletModel *parent) :
        QAbstractTableModel(parent),
        wallet(wallet),
        walletModel(parent),
        mintingInterval(10),
        priv(new MintingTablePriv(wallet, this)),
        cachedNumBlocks(0)
{
    columns << tr("Transaction") <<  tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability");

    priv->refreshWallet();

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateAge()));
    timer->start(MODEL_UPDATE_DELAY);

    connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
    wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
}

MintingTableModel::~MintingTableModel()
{
    wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
    delete priv;
}

void MintingTableModel::updateTransaction(const QString &hash, int status)
{
    uint256 updated;
    updated.SetHex(hash.toStdString());

    priv->updateWallet(updated, status);
    mintingProxyModel->invalidate(); // Force deletion of empty rows
}

void MintingTableModel::updateAge()
{
    Q_EMIT dataChanged(index(0, Age), index(priv->size()-1, Age));
    Q_EMIT dataChanged(index(0, CoinDay), index(priv->size()-1, CoinDay));
    Q_EMIT dataChanged(index(0, MintProbability), index(priv->size()-1, MintProbability));
}

void MintingTableModel::setMintingProxyModel(MintingFilterProxy *mintingProxy)
{
    mintingProxyModel = mintingProxy;
}

int MintingTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MintingTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MintingTableModel::data(const QModelIndex &index, int role) const
{
    const Consensus::Params& params = Params().GetConsensus();
    if(!index.isValid())
        return QVariant();
    KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());

    switch(role)
    {
      case Qt::DisplayRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return formatTxAge(rec);
        case Balance:
            return formatTxBalance(rec);
        case CoinDay:
            return formatTxCoinDay(rec);
        case MintProbability:
            return formatDayToMint(rec);
        }
        break;
      case Qt::TextAlignmentRole:
        return column_alignments[index.column()];
        break;
      case Qt::ToolTipRole:
        switch(index.column())
        {
        case MintProbability:
            int interval = this->mintingInterval;
            QString unit = tr("minutes");

            int hours = interval / 60;
            int days = hours  / 24;

            if(hours > 1) {
                interval = hours;
                unit = tr("hours");
            }
            if(days > 1) {
                interval = days;
                unit = tr("days");
            }

            QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
            return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
        }
        break;
      case Qt::EditRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return qint64(rec->getAge());
        case CoinDay:
            return qint64(rec->coinAge);
        case Balance:
            return qint64(rec->nValue);
        case MintProbability:
            return getDayToMint(rec);
        }
        break;
      case Qt::BackgroundColorRole:
        int minAge = params.nStakeMinAge / 60 / 60 / 24;
        int maxAge = params.nStakeMaxAge / 60 / 60 / 24;
        if(rec->getAge() < minAge)
        {
            return COLOR_MINT_YOUNG;
        }
        else if (rec->getAge() >= minAge && rec->getAge() < maxAge)
        {
            return COLOR_MINT_MATURE;
        }
        else
        {
            return COLOR_MINT_OLD;
        }
        break;

    }
    return QVariant();
}

void MintingTableModel::setMintingInterval(int interval)
{
    mintingInterval = interval;
}

QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
{
    QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
    QString description;
    if(!label.isEmpty())
    {
        description += label + QString(" ");
    }
    if(label.isEmpty() || tooltip)
    {
        description += QString(" (") + QString::fromStdString(address) + QString(")");
    }
    return description;
}

double MintingTableModel::getDayToMint(KernelRecord *wtx) const
{
    const CBlockIndex *p = GetLastBlockIndex(chainActive.Tip(), true);
    double difficulty = p->GetBlockDifficulty();

    double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
    prob = prob * 100;
    return prob;
}

QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
{
    double prob = getDayToMint(wtx);
    return QString::number(prob, 'f', 6) + "%";
}

QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
{
    return QString::fromStdString(wtx->address);
}

QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
{
    return QString::fromStdString(wtx->hash.ToString());
}

QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
{
    return QString::number(wtx->coinAge);
}

QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
{
    int64_t nAge = wtx->getAge();
    return QString::number(nAge);
}

QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
{
    return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
}

QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
        else if (role == Qt::TextAlignmentRole)
        {
            return column_alignments[section];
        } else if (role == Qt::ToolTipRole)
        {
            switch(section)
            {
            case Address:
                return tr("Destination address of the output.");
            case TxHash:
                return tr("Original transaction id.");
            case Age:
                return tr("Age of the transaction in days.");
            case Balance:
                return tr("Balance of the output.");
            case CoinDay:
                return tr("Coin age in the output.");
            case MintProbability:
                return tr("Chance to mint a block within given time interval.");
            }
        }
    }
    return QVariant();
}

QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    KernelRecord *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MintingTableModel::updateDisplayUnit()
{
    // emit dataChanged to update Balance column with the current unit
    Q_EMIT dataChanged(index(0, Balance), index(priv->size()-1, Balance));
}
<|endoftext|>


<commit_before>#include "transactionrecord.h"

#include "base58.h"
#include "timedata.h"
#include "wallet.h"

/* Return positive answer if transaction should be shown in list.
 */
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    int64_t nCredit = wtx.GetCredit(true);
    int64_t nDebit = wtx.GetDebit();
    int64_t nNet = nCredit - nDebit;
    uint256 hash = wtx.GetHash(), hashPrev = 0;
    std::map<std::string, std::string> mapValue = wtx.mapValue;

    if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake())
    {
        //
        // Credit
        //
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
        {
            if(wallet->IsMine(txout))
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.credit = txout.nValue;
                if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
                {
                    // Received by Bitcoin Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.address = mapValue["from"];
                }
                if (wtx.IsCoinBase())
                {
                    // Generated (proof-of-work)
                    sub.type = TransactionRecord::Generated;
                }
                if (wtx.IsCoinStake())
                {
                    // Generated (proof-of-stake)

                    if (hashPrev == hash)
                        continue; // last coinstake output

                    sub.type = TransactionRecord::Generated;
                    sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit;
                    hashPrev = hash;
                }

                parts.append(sub);
            }
        }
    }
    else
    {
        bool fAllFromMe = true;
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
            fAllFromMe = fAllFromMe && wallet->IsMine(txin);

        bool fAllToMe = true;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            fAllToMe = fAllToMe && wallet->IsMine(txout);

        if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            int64_t nChange = wtx.GetChange();

            parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
                            -(nDebit - nChange), nCredit - nChange));
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            int64_t nTxFee = nDebit - wtx.GetValueOut();

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut& txout = wtx.vout[nOut];
                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();

                if(wallet->IsMine(txout))
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                CTxDestination address;
                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to Bitcoin Address
                    sub.type = TransactionRecord::SendToAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    sub.address = mapValue["to"];
                }

                int64_t nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex* pindex = NULL;
    std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d",
        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0),
        wtx.nTimeReceived,
        idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = nBestHeight;

    if (!IsFinalTx(wtx, nBestHeight + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - nBestHeight;
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }

    // For generated transactions, determine maturity
    else if(type == TransactionRecord::Generated)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != nBestHeight;
}

QString TransactionRecord::getTxID() const
{
    return formatSubTxId(hash, idx);
}

QString TransactionRecord::formatSubTxId(const uint256 &hash, int vout)
{
    return QString::fromStdString(hash.ToString() + strprintf("-%03d", vout));
}

<commit_msg>Qt: Filter zero value entries from transaction list<commit_after>#include "transactionrecord.h"

#include "base58.h"
#include "timedata.h"
#include "wallet.h"

/* Return positive answer if transaction should be shown in list.
 */
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    int64_t nCredit = wtx.GetCredit(true);
    int64_t nDebit = wtx.GetDebit();
    int64_t nNet = nCredit - nDebit;
    uint256 hash = wtx.GetHash(), hashPrev = 0;
    std::map<std::string, std::string> mapValue = wtx.mapValue;

    if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake())
    {
        //
        // Credit
        //
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
        {
            if(wallet->IsMine(txout))
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.credit = txout.nValue;
                if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
                {
                    // Received by Bitcoin Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.address = mapValue["from"];
                }
                if (wtx.IsCoinBase())
                {
                    // Generated (proof-of-work)
                    sub.type = TransactionRecord::Generated;
                }
                if (wtx.IsCoinStake())
                {
                    // Generated (proof-of-stake)

                    if (hashPrev == hash)
                        continue; // last coinstake output

                    sub.type = TransactionRecord::Generated;
                    sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit;
                    hashPrev = hash;
                }

                if (sub.credit != 0)
				    parts.append(sub);
            }
        }
    }
    else
    {
        bool fAllFromMe = true;
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
            fAllFromMe = fAllFromMe && wallet->IsMine(txin);

        bool fAllToMe = true;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            fAllToMe = fAllToMe && wallet->IsMine(txout);

        if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            int64_t nChange = wtx.GetChange();

            parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
                            -(nDebit - nChange), nCredit - nChange));
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            int64_t nTxFee = nDebit - wtx.GetValueOut();

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut& txout = wtx.vout[nOut];
                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();

                if(wallet->IsMine(txout))
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                CTxDestination address;
                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to Bitcoin Address
                    sub.type = TransactionRecord::SendToAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    sub.address = mapValue["to"];
                }

                int64_t nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                if (sub.debit != 0)
				    parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex* pindex = NULL;
    std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d",
        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0),
        wtx.nTimeReceived,
        idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = nBestHeight;

    if (!IsFinalTx(wtx, nBestHeight + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - nBestHeight;
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }

    // For generated transactions, determine maturity
    else if(type == TransactionRecord::Generated)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != nBestHeight;
}

QString TransactionRecord::getTxID() const
{
    return formatSubTxId(hash, idx);
}

QString TransactionRecord::formatSubTxId(const uint256 &hash, int vout)
{
    return QString::fromStdString(hash.ToString() + strprintf("-%03d", vout));
}

<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------
// PSP Player Emulation Suite
// Copyright (C) 2006 Ben Vanik (noxa)
// Licensed under the LGPL - see License.txt in the project root for details
// ----------------------------------------------------------------------------

#include "StdAfx.h"
#include "R4000Cpu.h"
#include "R4000VideoInterface.h"
#include "VideoApi.h"

using namespace System::Diagnostics;
using namespace Noxa::Emulation::Psp;
using namespace Noxa::Emulation::Psp::Cpu;
using namespace Noxa::Emulation::Psp::Video::Native;

#ifdef _DEBUG
#define BREAK __break()
#define BREAKIF( cond ) if( cond ) __break();
#else
#define BREAK
#define BREAKIF( cond )
#endif

#define DEFAULTPACKETCAPACITY 50000

VdlRef* _outstandingLists;
VdlRef* _outstandingListsTail;

void* _memoryAddress;
VideoApi* _videoApi;
MemoryPool* _memoryPool;

void ClearOutstandingLists();

R4000VideoInterface::R4000VideoInterface( R4000Cpu^ cpu )
{
	_cpu = cpu;

	_outstandingLists = NULL;
	_outstandingListsTail = NULL;

	_pool = new MemoryPool();
	_memoryPool = _pool;
}

R4000VideoInterface::~R4000VideoInterface()
{
	ClearOutstandingLists();

	_memoryPool = NULL;
	SAFEDELETE( _pool );
}

void R4000VideoInterface::Prepare()
{
	_memoryAddress = _cpu->_memory->MainMemory;
	_videoApi = ( VideoApi* )_cpu->Emulator->Video->NativeInterface.ToPointer();
	if( _videoApi != NULL )
		_videoApi->Setup( _pool );
}

void __break()
{
	Debugger::Break();
}

#pragma unmanaged

void AddOutstandingList( VideoDisplayList* list )
{
	VdlRef* ref = ( VdlRef* )malloc( sizeof( VdlRef ) );
	ref->List = list;
	ref->Next = NULL;

	if( _outstandingLists == NULL )
		_outstandingLists = ref;
	if( _outstandingListsTail != NULL )
		_outstandingListsTail->Next = ref;
	_outstandingListsTail = ref;
}

VideoDisplayList* FindOutstandingList( int listId )
{
	VdlRef* ref = _outstandingLists;

	// Check tail first
	if( _outstandingListsTail != NULL )
	{
		if( _outstandingListsTail->List->ID == listId )
			return _outstandingListsTail->List;
	}

	while( ref != NULL )
	{
		if( ref->List->ID == listId )
			return ref->List;
		ref = ref->Next;
	}

	return NULL;
}

void RemoveOutstandingList( VideoDisplayList* list )
{
	VdlRef* ref = _outstandingLists;
	VdlRef* prev = NULL;

	while( ref != NULL )
	{
		if( ref->List == list )
		{
			if( prev != NULL )
				prev->Next = ref->Next;
			if( ref == _outstandingLists )
				_outstandingLists = ref->Next;
			if( ref == _outstandingListsTail )
				_outstandingListsTail = prev;
			SAFEFREE( ref );
			return;
		}
		prev = ref;
		ref = ref->Next;
	}
}

void ClearOutstandingLists()
{
	VdlRef* ref = _outstandingLists;
	while( ref != NULL )
	{
		VdlRef* next = ref->Next;
		SAFEFREE( ref );
		ref = next;
	}
	_outstandingLists = NULL;
	_outstandingListsTail = NULL;
}

void __inline GetVideoPacket( int code, int baseAddress, VideoPacket* packet )
{
	//packet->Command = ( byte )( code >> 24 );
	//packet->Argument = code & 0x00FFFFFF;
	*( ( int* )packet ) = code;

	switch( packet->Command )
	{
	case VADDR:
	case IADDR:
	case OFFSETADDR:
	case ORIGINADDR:
		packet->Argument = packet->Argument | baseAddress;
		break;
	}
}

bool ReadPackets( void* memoryAddress, uint pointer, uint stallAddress, VideoDisplayList* list )
{
	if( stallAddress == 0 )
		stallAddress = 0xFFFFFFFF;

	// Fix for cached addresses/etc
	pointer &= 0x3FFFFFFF;
	stallAddress &= 0x3FFFFFFF;

	int packetCount = list->PacketCount;

	int* memptr = ( int* )memoryAddress;
	while( pointer < stallAddress )
	{
		if( packetCount >= list->PacketCapacity )
		{
			// Ran out of space! No clue what to do! Ack!
			// Could we use MemoryPool to realloc and such?
			BREAK;
		}

		int code = *( ( int* )( ( byte* )memptr + ( pointer - 0x08000000 ) ) );
		VideoPacket* packet = list->Packets + packetCount;
		GetVideoPacket( code, list->Base, packet );
		packetCount++;

		pointer += 4;

		switch( packet->Command )
		{
		case BASE:
			list->Base = packet->Argument << 8;
			break;
		case JUMP:
			pointer = packet->Argument | list->Base;
			break;

			// Not implemented
		case CALL:
		case RET:
		case BJUMP:
			BREAK;
			break;

			// Stop conditions?
		case FINISH:
		case END:
		case Unknown0x11:
			list->PacketCount = packetCount;
			return true;
		}
	}

	list->PacketCount = packetCount;
	return false;
}

bool ReadMorePackets( void* memoryAddress, VideoDisplayList* vdl, uint newStallAddress )
{
	int oldStallAddress = vdl->StallAddress;
	vdl->StallAddress = newStallAddress;
	bool done = ReadPackets( memoryAddress, oldStallAddress, vdl->StallAddress, vdl );
	vdl->Ready = done;
	return done;
}

// sceGeUser -------------------------------------------

int sceGeListEnQueue( uint list, uint stall, int cbid, uint arg, int head )
{
	VideoDisplayList* vdl = ( VideoDisplayList* )malloc( sizeof( VideoDisplayList ) );
	memset( vdl, 0, sizeof( VideoDisplayList ) );
	vdl->CallbackID = cbid;
	vdl->Argument = arg;

	vdl->PacketCapacity = DEFAULTPACKETCAPACITY;
	vdl->Packets = ( VideoPacket* )_memoryPool->Request( DEFAULTPACKETCAPACITY * sizeof( VideoPacket ) );
#ifdef _DEBUG
	memset( vdl->Packets, 0, DEFAULTPACKETCAPACITY * sizeof( VideoPacket ) );
#endif

	if( stall == NULL )
	{
		vdl->Ready = true;

		// Read all now
		bool done = ReadPackets( _memoryAddress, list, 0, vdl );
		BREAKIF( done == false );
	}
	else
	{
		vdl->Ready = false;
		vdl->StallAddress = stall;

		// Rest will follow
		bool done = ReadPackets( _memoryAddress, list, stall, vdl );
		BREAKIF( done == true );

		AddOutstandingList( vdl );
	}

	VideoApi* ni = _videoApi;
	int listId = ni->EnqueueList( vdl, ( head == 1 ) ? true : false );
	if( listId >= 0 )
		return listId;
	else
		return -1;
}

int sceGeListDeQueue( int qid )
{
	VideoApi* ni = _videoApi;
	ni->DequeueList( qid );

	return 0;
}

int sceGeListUpdateStallAddr( int qid, uint stall )
{
	VideoApi* ni = _videoApi;

	VideoDisplayList* vdl = ni->FindList( qid );
	if( vdl == NULL )
	{
		BREAK;
		return -1;
	}

	if( vdl->Ready == false )
	{
		bool done = ReadMorePackets( _memoryAddress, vdl, stall );
		if( done == true )
		{
			RemoveOutstandingList( vdl );
			ni->SyncList( vdl->ID );
		}
	}

	return 0;
}

int sceGeListSync( int qid, int syncType )
{
	VideoApi* ni = _videoApi;

	VideoDisplayList* vdl = ni->FindList( qid );
	if( vdl == NULL )
	{
		// Could have been processed already
		return 0;
	}

	if( vdl->Ready == false )
	{
		bool done = ReadMorePackets( _memoryAddress, vdl, 0 );
		if( done == false )
			BREAK;
		else
		{
			RemoveOutstandingList( vdl );
			ni->SyncList( vdl->ID );
		}
	}

	return 0;
}

int sceGeDrawSync( int syncType )
{
	// Can only handle syncType == 0
	BREAKIF( syncType != 0 );

	// This a full sync - we need to finish all lists
	VdlRef* ref = _outstandingLists;
	while( ref != NULL )
	{
		VideoDisplayList* vdl = ref->List;
		if( vdl->Ready == false )
		{
			bool done = ReadMorePackets( _memoryAddress, vdl, 0 );
			BREAKIF( done == false );
		}
	}

	ClearOutstandingLists();

	VideoApi* ni = _videoApi;
	ni->Sync();

	return 0;
}

#pragma managed<commit_msg>Quick bugfixes in native video interface<commit_after>// ----------------------------------------------------------------------------
// PSP Player Emulation Suite
// Copyright (C) 2006 Ben Vanik (noxa)
// Licensed under the LGPL - see License.txt in the project root for details
// ----------------------------------------------------------------------------

#include "StdAfx.h"
#include "R4000Cpu.h"
#include "R4000VideoInterface.h"
#include "VideoApi.h"

using namespace System::Diagnostics;
using namespace Noxa::Emulation::Psp;
using namespace Noxa::Emulation::Psp::Cpu;
using namespace Noxa::Emulation::Psp::Video::Native;

#ifdef _DEBUG
#define BREAK __break()
#define BREAKIF( cond ) if( cond ) __break();
#else
#define BREAK
#define BREAKIF( cond )
#endif

#define DEFAULTPACKETCAPACITY 50000

VdlRef* _outstandingLists;
VdlRef* _outstandingListsTail;

void* _memoryAddress;
VideoApi* _videoApi;
MemoryPool* _memoryPool;

void ClearOutstandingLists();

R4000VideoInterface::R4000VideoInterface( R4000Cpu^ cpu )
{
	_cpu = cpu;

	_outstandingLists = NULL;
	_outstandingListsTail = NULL;

	_pool = new MemoryPool();
	_memoryPool = _pool;
}

R4000VideoInterface::~R4000VideoInterface()
{
	ClearOutstandingLists();

	_memoryPool = NULL;
	SAFEDELETE( _pool );
}

void R4000VideoInterface::Prepare()
{
	_memoryAddress = _cpu->_memory->MainMemory;
	_videoApi = ( VideoApi* )_cpu->Emulator->Video->NativeInterface.ToPointer();
	if( _videoApi != NULL )
		_videoApi->Setup( _pool );
}

void __break()
{
	Debugger::Break();
}

#pragma unmanaged

void AddOutstandingList( VideoDisplayList* list )
{
	VdlRef* ref = ( VdlRef* )malloc( sizeof( VdlRef ) );
	ref->List = list;
	ref->Next = NULL;

	if( _outstandingLists == NULL )
		_outstandingLists = ref;
	if( _outstandingListsTail != NULL )
		_outstandingListsTail->Next = ref;
	_outstandingListsTail = ref;
}

VideoDisplayList* FindOutstandingList( int listId )
{
	VdlRef* ref = _outstandingLists;

	// Check tail first
	if( _outstandingListsTail != NULL )
	{
		if( _outstandingListsTail->List->ID == listId )
			return _outstandingListsTail->List;
	}

	while( ref != NULL )
	{
		if( ref->List->ID == listId )
			return ref->List;
		ref = ref->Next;
	}

	return NULL;
}

void RemoveOutstandingList( int listId )
{
	VdlRef* ref = _outstandingLists;
	VdlRef* prev = NULL;

	while( ref != NULL )
	{
		if( ref->List->ID == listId )
		{
			if( prev != NULL )
				prev->Next = ref->Next;
			if( ref == _outstandingLists )
				_outstandingLists = ref->Next;
			if( ref == _outstandingListsTail )
				_outstandingListsTail = prev;
			SAFEFREE( ref );
			return;
		}
		prev = ref;
		ref = ref->Next;
	}
}

void RemoveOutstandingList( VideoDisplayList* list )
{
	VdlRef* ref = _outstandingLists;
	VdlRef* prev = NULL;

	while( ref != NULL )
	{
		if( ref->List == list )
		{
			if( prev != NULL )
				prev->Next = ref->Next;
			if( ref == _outstandingLists )
				_outstandingLists = ref->Next;
			if( ref == _outstandingListsTail )
				_outstandingListsTail = prev;
			SAFEFREE( ref );
			return;
		}
		prev = ref;
		ref = ref->Next;
	}
}

void ClearOutstandingLists()
{
	VdlRef* ref = _outstandingLists;
	while( ref != NULL )
	{
		VdlRef* next = ref->Next;
		SAFEFREE( ref );
		ref = next;
	}
	_outstandingLists = NULL;
	_outstandingListsTail = NULL;
}

void __inline GetVideoPacket( int code, int baseAddress, VideoPacket* packet )
{
	//packet->Command = ( byte )( code >> 24 );
	//packet->Argument = code & 0x00FFFFFF;
	*( ( int* )packet ) = code;

	switch( packet->Command )
	{
	case VADDR:
	case IADDR:
	case OFFSETADDR:
	case ORIGINADDR:
		packet->Argument = packet->Argument | baseAddress;
		break;
	}
}

bool ReadPackets( void* memoryAddress, uint pointer, uint stallAddress, VideoDisplayList* list )
{
	if( stallAddress == 0 )
		stallAddress = 0xFFFFFFFF;

	// Fix for cached addresses/etc
	pointer &= 0x3FFFFFFF;
	stallAddress &= 0x3FFFFFFF;

	int packetCount = list->PacketCount;

	int* memptr = ( int* )memoryAddress;
	while( pointer < stallAddress )
	{
		if( packetCount >= list->PacketCapacity )
		{
			// Ran out of space! No clue what to do! Ack!
			// Could we use MemoryPool to realloc and such?
			BREAK;
		}

		int code = *( ( int* )( ( byte* )memptr + ( pointer - 0x08000000 ) ) );
		VideoPacket* packet = list->Packets + packetCount;
		GetVideoPacket( code, list->Base, packet );
		packetCount++;

		pointer += 4;

		switch( packet->Command )
		{
		case BASE:
			list->Base = packet->Argument << 8;
			break;
		case JUMP:
			pointer = packet->Argument | list->Base;
			break;

			// Not implemented
		case CALL:
		case RET:
		case BJUMP:
			BREAK;
			break;

			// Stop conditions?
		case FINISH:
		case END:
		case Unknown0x11:
			list->PacketCount = packetCount;
			return true;
		}
	}

	list->PacketCount = packetCount;
	return false;
}

bool ReadMorePackets( void* memoryAddress, VideoDisplayList* vdl, uint newStallAddress )
{
	int oldStallAddress = vdl->StallAddress;
	vdl->StallAddress = newStallAddress;
	bool done = ReadPackets( memoryAddress, oldStallAddress, vdl->StallAddress, vdl );
	vdl->Ready = done;
	return done;
}

// sceGeUser -------------------------------------------

int sceGeListEnQueue( uint list, uint stall, int cbid, uint arg, int head )
{
	VideoDisplayList* vdl = ( VideoDisplayList* )malloc( sizeof( VideoDisplayList ) );
	memset( vdl, 0, sizeof( VideoDisplayList ) );
	vdl->CallbackID = cbid;
	vdl->Argument = arg;

	vdl->PacketCapacity = DEFAULTPACKETCAPACITY;
	vdl->Packets = ( VideoPacket* )_memoryPool->Request( DEFAULTPACKETCAPACITY * sizeof( VideoPacket ) );
#ifdef _DEBUG
	memset( vdl->Packets, 0, DEFAULTPACKETCAPACITY * sizeof( VideoPacket ) );
#endif

	if( stall == NULL )
	{
		vdl->Ready = true;

		// Read all now
		bool done = ReadPackets( _memoryAddress, list, 0, vdl );
		BREAKIF( done == false );
	}
	else
	{
		vdl->Ready = false;
		vdl->StallAddress = stall;

		// Rest will follow
		bool done = ReadPackets( _memoryAddress, list, stall, vdl );
		BREAKIF( done == true );

		AddOutstandingList( vdl );
	}

	VideoApi* ni = _videoApi;
	int listId = ni->EnqueueList( vdl, ( head == 1 ) ? true : false );
	if( listId >= 0 )
		return listId;
	else
		return -1;
}

int sceGeListDeQueue( int qid )
{
	VideoApi* ni = _videoApi;

	RemoveOutstandingList( qid );
	ni->DequeueList( qid );

	return 0;
}

int sceGeListUpdateStallAddr( int qid, uint stall )
{
	VideoApi* ni = _videoApi;

	//VideoDisplayList* vdl = ni->FindList( qid );
	VideoDisplayList* vdl = FindOutstandingList( qid );
	if( vdl == NULL )
	{
		BREAK;
		return -1;
	}

	if( vdl->Ready == false )
	{
		bool done = ReadMorePackets( _memoryAddress, vdl, stall );
		if( done == true )
		{
			RemoveOutstandingList( vdl );
			ni->SyncList( vdl->ID );
		}
	}

	return 0;
}

int sceGeListSync( int qid, int syncType )
{
	VideoApi* ni = _videoApi;

	//VideoDisplayList* vdl = ni->FindList( qid );
	VideoDisplayList* vdl = FindOutstandingList( qid );
	if( vdl == NULL )
	{
		// Could have been processed already
		return 0;
	}

	if( vdl->Ready == false )
	{
		bool done = ReadMorePackets( _memoryAddress, vdl, 0 );
		if( done == false )
			BREAK;
		else
		{
			RemoveOutstandingList( vdl );
			ni->SyncList( vdl->ID );
		}
	}

	return 0;
}

int sceGeDrawSync( int syncType )
{
	// Can only handle syncType == 0
	BREAKIF( syncType != 0 );

	// This a full sync - we need to finish all lists
	VdlRef* ref = _outstandingLists;
	while( ref != NULL )
	{
		VideoDisplayList* vdl = ref->List;
		if( vdl->Ready == false )
		{
			bool done = ReadMorePackets( _memoryAddress, vdl, 0 );
			BREAKIF( done == false );
		}
		ref = ref->Next;
	}

	ClearOutstandingLists();

	VideoApi* ni = _videoApi;
	ni->Sync();

	return 0;
}

#pragma managed<|endoftext|>