OpenMSXConnection.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #include "OpenMSXConnection.h"
  2. #include <QXmlInputSource>
  3. #include <QXmlSimpleReader>
  4. #include <cassert>
  5. SimpleCommand::SimpleCommand(const QString& command_)
  6. : command(command_)
  7. {
  8. }
  9. QString SimpleCommand::getCommand() const
  10. {
  11. return command;
  12. }
  13. void SimpleCommand::replyOk (const QString& message)
  14. {
  15. emit replyStatusOk(true);
  16. delete this;
  17. }
  18. void SimpleCommand::replyNok(const QString& message)
  19. {
  20. emit replyStatusOk(false);
  21. cancel();
  22. }
  23. void SimpleCommand::cancel()
  24. {
  25. delete this;
  26. }
  27. static QString createDebugCommand(const QString& debuggable,
  28. unsigned offset, unsigned size)
  29. {
  30. return QString("debug_bin2hex [ debug read_block %1 %2 %3 ]")
  31. .arg(debuggable).arg(offset).arg(size);
  32. }
  33. ReadDebugBlockCommand::ReadDebugBlockCommand(const QString& commandString,
  34. unsigned size_, unsigned char* target_)
  35. : SimpleCommand(commandString)
  36. , size(size_), target(target_)
  37. {
  38. }
  39. ReadDebugBlockCommand::ReadDebugBlockCommand(const QString& debuggable,
  40. unsigned offset, unsigned size_, unsigned char* target_)
  41. : SimpleCommand(createDebugCommand(debuggable, offset, size_))
  42. , size(size_), target(target_)
  43. {
  44. }
  45. static QString createDebugWriteCommand(const QString& debuggable,
  46. unsigned offset, unsigned size, unsigned char *data )
  47. {
  48. QString cmd = QString("debug write_block %1 %2 [ debug_hex2bin \"")
  49. .arg(debuggable).arg(offset);
  50. for (unsigned i = offset; i < offset + size; ++i) {
  51. cmd += QString("%1").arg(int(data[i]), 2, 16, QChar('0')).toUpper();
  52. }
  53. cmd += "\" ]";
  54. return cmd;
  55. }
  56. WriteDebugBlockCommand::WriteDebugBlockCommand(const QString& debuggable,
  57. unsigned offset, unsigned size_, unsigned char* source_)
  58. : SimpleCommand(createDebugWriteCommand(debuggable, offset, size_, source_))
  59. {
  60. }
  61. static unsigned char hex2val(char c)
  62. {
  63. return (c <= '9') ? (c - '0') : (c - 'A' + 10);
  64. }
  65. void ReadDebugBlockCommand::copyData(const QString& message)
  66. {
  67. assert(static_cast<unsigned>(message.size()) == 2 * size);
  68. for (unsigned i = 0; i < size; ++i) {
  69. target[i] = (hex2val(message[2 * i + 0].toLatin1()) << 4) +
  70. (hex2val(message[2 * i + 1].toLatin1()) << 0);
  71. }
  72. }
  73. OpenMSXConnection::OpenMSXConnection(QAbstractSocket* socket_)
  74. : socket(socket_)
  75. , reader(new QXmlSimpleReader())
  76. , connected(true)
  77. {
  78. assert(socket->isValid());
  79. reader->setContentHandler(this);
  80. reader->setErrorHandler(this);
  81. connect(socket, SIGNAL(readyRead()), this, SLOT(processData()));
  82. connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
  83. this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
  84. connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
  85. this, SLOT(socketError(QAbstractSocket::SocketError)));
  86. socket->write("<openmsx-control>\n");
  87. }
  88. OpenMSXConnection::~OpenMSXConnection()
  89. {
  90. cleanup();
  91. assert(commands.empty());
  92. assert(!connected);
  93. socket->deleteLater();
  94. }
  95. void OpenMSXConnection::sendCommand(Command* command)
  96. {
  97. assert(command);
  98. if (connected && socket->isValid()) {
  99. commands.enqueue(command);
  100. QString cmd = "<command>" + command->getCommand() + "</command>";
  101. socket->write(cmd.toUtf8());
  102. } else {
  103. command->cancel();
  104. }
  105. }
  106. void OpenMSXConnection::cleanup()
  107. {
  108. if (!connected) return;
  109. connected = false;
  110. if (socket->isValid()) {
  111. socket->disconnect(this, SLOT(processData()));
  112. socket->disconnect(this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
  113. socket->disconnect(this, SLOT(socketError(QAbstractSocket::SocketError)));
  114. socket->write("</openmsx-control>\n");
  115. socket->disconnectFromHost();
  116. }
  117. cancelPending();
  118. emit disconnected();
  119. }
  120. void OpenMSXConnection::cancelPending()
  121. {
  122. assert(!connected);
  123. while (!commands.empty()) {
  124. Command* command = commands.dequeue();
  125. command->cancel();
  126. }
  127. }
  128. void OpenMSXConnection::socketStateChanged(QAbstractSocket::SocketState state)
  129. {
  130. if (state != QAbstractSocket::ConnectedState) {
  131. cleanup();
  132. }
  133. }
  134. void OpenMSXConnection::socketError(QAbstractSocket::SocketError /*state*/)
  135. {
  136. cleanup();
  137. }
  138. void OpenMSXConnection::processData()
  139. {
  140. if (input.get()) {
  141. // continue
  142. input->setData(socket->readAll());
  143. reader->parseContinue();
  144. } else {
  145. // first time
  146. input.reset(new QXmlInputSource());
  147. input->setData(socket->readAll());
  148. reader->parse(input.get(), true); // incremental parsing
  149. }
  150. }
  151. bool OpenMSXConnection::fatalError(const QXmlParseException& exception)
  152. {
  153. qWarning("Fatal error on line %i, column %i: %s",
  154. exception.lineNumber(), exception.columnNumber(),
  155. exception.message().toLatin1().data());
  156. cleanup();
  157. return false;
  158. }
  159. bool OpenMSXConnection::startElement(
  160. const QString& /*namespaceURI*/, const QString& /*localName*/,
  161. const QString& /*qName*/, const QXmlAttributes& atts)
  162. {
  163. xmlAttrs = atts;
  164. xmlData.clear();
  165. return true;
  166. }
  167. bool OpenMSXConnection::endElement(
  168. const QString& /*namespaceURI*/, const QString& /*localName*/,
  169. const QString& qName)
  170. {
  171. if (qName == "openmsx-output") {
  172. // ignore
  173. } else if (qName == "reply") {
  174. if (connected) {
  175. Command* command = commands.dequeue();
  176. if (xmlAttrs.value("result") == "ok") {
  177. command->replyOk (xmlData);
  178. } else {
  179. command->replyNok(xmlData);
  180. }
  181. } else {
  182. // still receive a reply while we're already closing
  183. // the connection, ignore it
  184. }
  185. } else if (qName == "log") {
  186. emit logParsed(xmlAttrs.value("level"), xmlData);
  187. } else if (qName == "update") {
  188. emit updateParsed(xmlAttrs.value("type"), xmlAttrs.value("name"), xmlData);
  189. } else {
  190. qWarning("Unknown XML tag: %s", qName.toLatin1().data());
  191. }
  192. return true;
  193. }
  194. bool OpenMSXConnection::characters(const QString& ch)
  195. {
  196. xmlData += ch;
  197. return true;
  198. }