Basic debugger networking.
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/debug/debug_server.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
#define XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
@@ -10,14 +10,28 @@
|
||||
#include "xenia/debug/debugger.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <mstcpip.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/emulator.h"
|
||||
|
||||
// Autogenerated Flatbuffers files:
|
||||
#include "xenia/debug/proto/breakpoints_generated.h"
|
||||
#include "xenia/debug/proto/messages_generated.h"
|
||||
|
||||
DEFINE_string(debug_session_path, "", "Debug output path.");
|
||||
DEFINE_int32(debug_port, 19000, "Port the debugger listens on.");
|
||||
DEFINE_bool(wait_for_debugger, false,
|
||||
"Waits for the debugger to attach before starting the game.");
|
||||
DEFINE_bool(exit_with_debugger, true, "Exit whe the debugger disconnects.");
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
@@ -29,9 +43,19 @@ Breakpoint::Breakpoint(Type type, uint32_t address)
|
||||
|
||||
Breakpoint::~Breakpoint() = default;
|
||||
|
||||
Debugger::Debugger(cpu::Processor* processor) : processor_(processor) {}
|
||||
Debugger::Debugger(Emulator* emulator)
|
||||
: emulator_(emulator),
|
||||
listen_socket_(INVALID_SOCKET),
|
||||
client_socket_(INVALID_SOCKET) {
|
||||
WSADATA wsa_data;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
}
|
||||
|
||||
Debugger::~Debugger() = default;
|
||||
Debugger::~Debugger() {
|
||||
if (listen_socket_ != INVALID_SOCKET) {
|
||||
StopSession();
|
||||
}
|
||||
}
|
||||
|
||||
bool Debugger::StartSession() {
|
||||
std::wstring session_path = xe::to_wstring(FLAGS_debug_session_path);
|
||||
@@ -44,11 +68,194 @@ bool Debugger::StartSession() {
|
||||
xe::join_paths(session_path, L"functions.trace");
|
||||
functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
|
||||
functions_trace_path, 32 * 1024 * 1024, true);
|
||||
|
||||
listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (listen_socket_ < 1) {
|
||||
XELOGE("Failed to create debugger socket");
|
||||
return false;
|
||||
}
|
||||
struct tcp_keepalive alive;
|
||||
alive.onoff = TRUE;
|
||||
alive.keepalivetime = 7200000;
|
||||
alive.keepaliveinterval = 6000;
|
||||
DWORD bytes_returned;
|
||||
WSAIoctl(listen_socket_, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), nullptr,
|
||||
0, &bytes_returned, nullptr, nullptr);
|
||||
int opt_value = 1;
|
||||
setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char*>(&opt_value), sizeof(opt_value));
|
||||
opt_value = 1;
|
||||
setsockopt(listen_socket_, IPPROTO_TCP, TCP_NODELAY,
|
||||
reinterpret_cast<const char*>(&opt_value), sizeof(opt_value));
|
||||
|
||||
sockaddr_in socket_addr = {0};
|
||||
socket_addr.sin_family = AF_INET;
|
||||
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
socket_addr.sin_port = htons(FLAGS_debug_port);
|
||||
if (bind(listen_socket_, reinterpret_cast<sockaddr*>(&socket_addr),
|
||||
sizeof(socket_addr)) == SOCKET_ERROR) {
|
||||
int e = WSAGetLastError();
|
||||
XELOGE("Unable to bind debug socket");
|
||||
return false;
|
||||
}
|
||||
if (listen(listen_socket_, 5) == SOCKET_ERROR) {
|
||||
XELOGE("Unable to listen on debug socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendResponse(SOCKET client_socket, flatbuffers::FlatBufferBuilder& fbb,
|
||||
uint32_t request_id, proto::ResponseData response_data_type,
|
||||
flatbuffers::Offset<void> response_data_offset) {
|
||||
auto response_offset = proto::CreateResponse(
|
||||
fbb, request_id, response_data_type, response_data_offset);
|
||||
fbb.Finish(response_offset);
|
||||
int buffer_length = fbb.GetSize();
|
||||
send(client_socket, reinterpret_cast<const char*>(&buffer_length), 4, 0);
|
||||
send(client_socket, reinterpret_cast<const char*>(fbb.GetBufferPointer()),
|
||||
fbb.GetSize(), 0);
|
||||
}
|
||||
|
||||
void Debugger::PreLaunch() {
|
||||
accept_thread_ = std::thread([this]() {
|
||||
while (listen_socket_ != INVALID_SOCKET) {
|
||||
sockaddr_in6 client_addr;
|
||||
int client_count = sizeof(client_addr);
|
||||
SOCKET client_socket_id =
|
||||
accept(listen_socket_, reinterpret_cast<sockaddr*>(&client_addr),
|
||||
&client_count);
|
||||
if (client_socket_id == INVALID_SOCKET) {
|
||||
XELOGE("Failed to accept socket");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only one debugger at a time.
|
||||
if (client_socket_ != INVALID_SOCKET) {
|
||||
XELOGW("Ignoring debugger connection as one is already connected");
|
||||
closesocket(client_socket_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Setup recv thread.
|
||||
client_socket_ = client_socket_id;
|
||||
receive_thread_ = std::thread([this]() {
|
||||
while (client_socket_ != INVALID_SOCKET) {
|
||||
// Read length prefix.
|
||||
uint32_t length = 0;
|
||||
int r = recv(client_socket_, reinterpret_cast<char*>(&length), 4,
|
||||
MSG_WAITALL);
|
||||
if (r != 4) {
|
||||
// Failed?
|
||||
XELOGE("Failed to recv debug data length - dead connection?");
|
||||
if (FLAGS_exit_with_debugger) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Read body.
|
||||
std::vector<uint8_t> body(length);
|
||||
r = recv(client_socket_, reinterpret_cast<char*>(body.data()), length,
|
||||
MSG_WAITALL);
|
||||
if (r != length) {
|
||||
// Failed?
|
||||
XELOGE("Failed to recv debug data body - dead connection?");
|
||||
if (FLAGS_exit_with_debugger) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Read message contents and dispatch.
|
||||
OnMessage(std::move(body));
|
||||
}
|
||||
});
|
||||
|
||||
// This will WaitForClient if it was waiting.
|
||||
}
|
||||
});
|
||||
|
||||
if (FLAGS_wait_for_debugger) {
|
||||
// Wait for the first client.
|
||||
XELOGI("Waiting for debugger because of --wait_for_debugger...");
|
||||
accept_fence_.Wait();
|
||||
XELOGI("Debugger attached, continuing...");
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::OnMessage(std::vector<uint8_t> buffer) {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
proto::ResponseData response_data_type;
|
||||
flatbuffers::Offset<void> response_data_offset;
|
||||
|
||||
auto request = flatbuffers::GetRoot<proto::Request>(buffer.data());
|
||||
switch (request->request_data_type()) {
|
||||
case proto::RequestData_AttachRequest: {
|
||||
// Send debug info.
|
||||
response_data_type = proto::ResponseData_AttachResponse;
|
||||
auto response_data = proto::AttachResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
|
||||
// Allow continuation if we were blocked waiting for a client.
|
||||
accept_fence_.Signal();
|
||||
} break;
|
||||
|
||||
case proto::RequestData_ListBreakpointsRequest: {
|
||||
response_data_type = proto::ResponseData_ListBreakpointsResponse;
|
||||
auto response_data = proto::ListBreakpointsResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
case proto::RequestData_AddBreakpointRequest: {
|
||||
response_data_type = proto::ResponseData_RemoveBreakpointResponse;
|
||||
auto response_data = proto::AddBreakpointResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
case proto::RequestData_UpdateBreakpointRequest: {
|
||||
response_data_type = proto::ResponseData_UpdateBreakpointResponse;
|
||||
auto response_data = proto::UpdateBreakpointResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
case proto::RequestData_RemoveBreakpointRequest: {
|
||||
response_data_type = proto::ResponseData_AddBreakpointResponse;
|
||||
auto response_data = proto::RemoveBreakpointResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert_unhandled_case(request->request_data_type());
|
||||
break;
|
||||
}
|
||||
|
||||
SendResponse(client_socket_, std::move(fbb), request->id(),
|
||||
response_data_type, response_data_offset);
|
||||
}
|
||||
|
||||
void Debugger::StopSession() {
|
||||
FlushSession();
|
||||
|
||||
if (client_socket_ != INVALID_SOCKET) {
|
||||
shutdown(client_socket_, SD_SEND);
|
||||
closesocket(client_socket_);
|
||||
client_socket_ = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
linger so_linger;
|
||||
so_linger.l_onoff = TRUE;
|
||||
so_linger.l_linger = 30;
|
||||
setsockopt(listen_socket_, SOL_SOCKET, SO_LINGER,
|
||||
reinterpret_cast<const char*>(&so_linger), sizeof(so_linger));
|
||||
shutdown(listen_socket_, SD_SEND);
|
||||
closesocket(listen_socket_);
|
||||
listen_socket_ = INVALID_SOCKET;
|
||||
accept_thread_.join();
|
||||
|
||||
functions_file_.reset();
|
||||
functions_trace_file_.reset();
|
||||
}
|
||||
@@ -132,7 +339,8 @@ int Debugger::AddBreakpoint(Breakpoint* breakpoint) {
|
||||
}
|
||||
|
||||
// Find all functions that contain the breakpoint address.
|
||||
auto fns = processor_->FindFunctionsWithAddress(breakpoint->address());
|
||||
auto fns =
|
||||
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
||||
|
||||
// Add.
|
||||
for (auto fn : fns) {
|
||||
@@ -166,7 +374,8 @@ int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) {
|
||||
}
|
||||
|
||||
// Find all functions that have the breakpoint set.
|
||||
auto fns = processor_->FindFunctionsWithAddress(breakpoint->address());
|
||||
auto fns =
|
||||
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
||||
|
||||
// Remove.
|
||||
for (auto fn : fns) {
|
||||
|
||||
@@ -14,13 +14,19 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/delegate.h"
|
||||
#include "xenia/base/mapped_memory.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/debug/breakpoint.h"
|
||||
|
||||
namespace xe {
|
||||
class Emulator;
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class Function;
|
||||
@@ -62,12 +68,13 @@ class BreakpointHitEvent : public DebugEvent {
|
||||
|
||||
class Debugger {
|
||||
public:
|
||||
Debugger(cpu::Processor* processor);
|
||||
Debugger(Emulator* emulator);
|
||||
~Debugger();
|
||||
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
|
||||
bool StartSession();
|
||||
void PreLaunch();
|
||||
void StopSession();
|
||||
void FlushSession();
|
||||
|
||||
@@ -99,7 +106,15 @@ class Debugger {
|
||||
Delegate<BreakpointHitEvent> breakpoint_hit;
|
||||
|
||||
private:
|
||||
cpu::Processor* processor_;
|
||||
void OnMessage(std::vector<uint8_t> buffer);
|
||||
|
||||
Emulator* emulator_;
|
||||
|
||||
UINT_PTR listen_socket_;
|
||||
std::thread accept_thread_;
|
||||
xe::threading::Fence accept_fence_;
|
||||
UINT_PTR client_socket_;
|
||||
std::thread receive_thread_;
|
||||
|
||||
std::unique_ptr<ChunkedMappedMemoryWriter> functions_file_;
|
||||
std::unique_ptr<ChunkedMappedMemoryWriter> functions_trace_file_;
|
||||
|
||||
23
src/xenia/debug/proto/breakpoints.fbs
Normal file
23
src/xenia/debug/proto/breakpoints.fbs
Normal file
@@ -0,0 +1,23 @@
|
||||
include "common.fbs";
|
||||
|
||||
namespace xe.debug.proto;
|
||||
|
||||
table ListBreakpointsRequest {
|
||||
}
|
||||
table ListBreakpointsResponse {
|
||||
}
|
||||
|
||||
table AddBreakpointRequest {
|
||||
}
|
||||
table AddBreakpointResponse {
|
||||
}
|
||||
|
||||
table UpdateBreakpointRequest {
|
||||
}
|
||||
table UpdateBreakpointResponse {
|
||||
}
|
||||
|
||||
table RemoveBreakpointRequest {
|
||||
}
|
||||
table RemoveBreakpointResponse {
|
||||
}
|
||||
210
src/xenia/debug/proto/breakpoints_generated.h
Normal file
210
src/xenia/debug/proto/breakpoints_generated.h
Normal file
@@ -0,0 +1,210 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_BREAKPOINTS_XE_DEBUG_PROTO_H_
|
||||
#define FLATBUFFERS_GENERATED_BREAKPOINTS_XE_DEBUG_PROTO_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
|
||||
struct ListBreakpointsRequest;
|
||||
struct ListBreakpointsResponse;
|
||||
struct AddBreakpointRequest;
|
||||
struct AddBreakpointResponse;
|
||||
struct UpdateBreakpointRequest;
|
||||
struct UpdateBreakpointResponse;
|
||||
struct RemoveBreakpointRequest;
|
||||
struct RemoveBreakpointResponse;
|
||||
|
||||
struct ListBreakpointsRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ListBreakpointsRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
ListBreakpointsRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
ListBreakpointsRequestBuilder &operator=(const ListBreakpointsRequestBuilder &);
|
||||
flatbuffers::Offset<ListBreakpointsRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<ListBreakpointsRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<ListBreakpointsRequest> CreateListBreakpointsRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
ListBreakpointsRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct ListBreakpointsResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ListBreakpointsResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
ListBreakpointsResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
ListBreakpointsResponseBuilder &operator=(const ListBreakpointsResponseBuilder &);
|
||||
flatbuffers::Offset<ListBreakpointsResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<ListBreakpointsResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<ListBreakpointsResponse> CreateListBreakpointsResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
ListBreakpointsResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AddBreakpointRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AddBreakpointRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AddBreakpointRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AddBreakpointRequestBuilder &operator=(const AddBreakpointRequestBuilder &);
|
||||
flatbuffers::Offset<AddBreakpointRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<AddBreakpointRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AddBreakpointRequest> CreateAddBreakpointRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AddBreakpointRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AddBreakpointResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AddBreakpointResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AddBreakpointResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AddBreakpointResponseBuilder &operator=(const AddBreakpointResponseBuilder &);
|
||||
flatbuffers::Offset<AddBreakpointResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<AddBreakpointResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AddBreakpointResponse> CreateAddBreakpointResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AddBreakpointResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct UpdateBreakpointRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateBreakpointRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
UpdateBreakpointRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
UpdateBreakpointRequestBuilder &operator=(const UpdateBreakpointRequestBuilder &);
|
||||
flatbuffers::Offset<UpdateBreakpointRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<UpdateBreakpointRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<UpdateBreakpointRequest> CreateUpdateBreakpointRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
UpdateBreakpointRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct UpdateBreakpointResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateBreakpointResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
UpdateBreakpointResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
UpdateBreakpointResponseBuilder &operator=(const UpdateBreakpointResponseBuilder &);
|
||||
flatbuffers::Offset<UpdateBreakpointResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<UpdateBreakpointResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<UpdateBreakpointResponse> CreateUpdateBreakpointResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
UpdateBreakpointResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct RemoveBreakpointRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveBreakpointRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
RemoveBreakpointRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
RemoveBreakpointRequestBuilder &operator=(const RemoveBreakpointRequestBuilder &);
|
||||
flatbuffers::Offset<RemoveBreakpointRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<RemoveBreakpointRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<RemoveBreakpointRequest> CreateRemoveBreakpointRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
RemoveBreakpointRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct RemoveBreakpointResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveBreakpointResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
RemoveBreakpointResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
RemoveBreakpointResponseBuilder &operator=(const RemoveBreakpointResponseBuilder &);
|
||||
flatbuffers::Offset<RemoveBreakpointResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<RemoveBreakpointResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<RemoveBreakpointResponse> CreateRemoveBreakpointResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
RemoveBreakpointResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
} // namespace proto
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_BREAKPOINTS_XE_DEBUG_PROTO_H_
|
||||
1
src/xenia/debug/proto/common.fbs
Normal file
1
src/xenia/debug/proto/common.fbs
Normal file
@@ -0,0 +1 @@
|
||||
namespace xe.debug.proto;
|
||||
3
src/xenia/debug/proto/control.fbs
Normal file
3
src/xenia/debug/proto/control.fbs
Normal file
@@ -0,0 +1,3 @@
|
||||
include "common.fbs";
|
||||
|
||||
namespace xe.debug.proto;
|
||||
59
src/xenia/debug/proto/messages.fbs
Normal file
59
src/xenia/debug/proto/messages.fbs
Normal file
@@ -0,0 +1,59 @@
|
||||
include "breakpoints.fbs";
|
||||
include "common.fbs";
|
||||
include "control.fbs";
|
||||
|
||||
namespace xe.debug.proto;
|
||||
|
||||
enum Foo:byte (bit_flags) {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
struct StructTest {
|
||||
a:short;
|
||||
b:byte;
|
||||
c:Foo;
|
||||
}
|
||||
|
||||
table TableTest {
|
||||
st:StructTest;
|
||||
iv:[ubyte];
|
||||
name:string (required);
|
||||
id:uint (key);
|
||||
}
|
||||
|
||||
table AttachRequest {
|
||||
//
|
||||
}
|
||||
table AttachResponse {
|
||||
// file paths
|
||||
// mmap name
|
||||
}
|
||||
|
||||
union RequestData {
|
||||
AttachRequest,
|
||||
|
||||
ListBreakpointsRequest,
|
||||
AddBreakpointRequest,
|
||||
UpdateBreakpointRequest,
|
||||
RemoveBreakpointRequest,
|
||||
}
|
||||
|
||||
table Request {
|
||||
id:uint;
|
||||
request_data:RequestData;
|
||||
}
|
||||
|
||||
union ResponseData {
|
||||
AttachResponse,
|
||||
|
||||
ListBreakpointsResponse,
|
||||
AddBreakpointResponse,
|
||||
UpdateBreakpointResponse,
|
||||
RemoveBreakpointResponse,
|
||||
}
|
||||
|
||||
table Response {
|
||||
id:uint;
|
||||
response_data:ResponseData;
|
||||
}
|
||||
298
src/xenia/debug/proto/messages_generated.h
Normal file
298
src/xenia/debug/proto/messages_generated.h
Normal file
@@ -0,0 +1,298 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_MESSAGES_XE_DEBUG_PROTO_H_
|
||||
#define FLATBUFFERS_GENERATED_MESSAGES_XE_DEBUG_PROTO_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
struct ListBreakpointsRequest;
|
||||
struct ListBreakpointsResponse;
|
||||
struct AddBreakpointRequest;
|
||||
struct AddBreakpointResponse;
|
||||
struct UpdateBreakpointRequest;
|
||||
struct UpdateBreakpointResponse;
|
||||
struct RemoveBreakpointRequest;
|
||||
struct RemoveBreakpointResponse;
|
||||
} // namespace proto
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
|
||||
struct StructTest;
|
||||
struct TableTest;
|
||||
struct AttachRequest;
|
||||
struct AttachResponse;
|
||||
struct Request;
|
||||
struct Response;
|
||||
|
||||
enum Foo {
|
||||
Foo_A = 1,
|
||||
Foo_B = 2
|
||||
};
|
||||
|
||||
inline const char **EnumNamesFoo() {
|
||||
static const char *names[] = { "A", "B", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameFoo(Foo e) { return EnumNamesFoo()[e - Foo_A]; }
|
||||
|
||||
enum RequestData {
|
||||
RequestData_NONE = 0,
|
||||
RequestData_AttachRequest = 1,
|
||||
RequestData_ListBreakpointsRequest = 2,
|
||||
RequestData_AddBreakpointRequest = 3,
|
||||
RequestData_UpdateBreakpointRequest = 4,
|
||||
RequestData_RemoveBreakpointRequest = 5
|
||||
};
|
||||
|
||||
inline const char **EnumNamesRequestData() {
|
||||
static const char *names[] = { "NONE", "AttachRequest", "ListBreakpointsRequest", "AddBreakpointRequest", "UpdateBreakpointRequest", "RemoveBreakpointRequest", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameRequestData(RequestData e) { return EnumNamesRequestData()[e]; }
|
||||
|
||||
inline bool VerifyRequestData(flatbuffers::Verifier &verifier, const void *union_obj, RequestData type);
|
||||
|
||||
enum ResponseData {
|
||||
ResponseData_NONE = 0,
|
||||
ResponseData_AttachResponse = 1,
|
||||
ResponseData_ListBreakpointsResponse = 2,
|
||||
ResponseData_AddBreakpointResponse = 3,
|
||||
ResponseData_UpdateBreakpointResponse = 4,
|
||||
ResponseData_RemoveBreakpointResponse = 5
|
||||
};
|
||||
|
||||
inline const char **EnumNamesResponseData() {
|
||||
static const char *names[] = { "NONE", "AttachResponse", "ListBreakpointsResponse", "AddBreakpointResponse", "UpdateBreakpointResponse", "RemoveBreakpointResponse", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameResponseData(ResponseData e) { return EnumNamesResponseData()[e]; }
|
||||
|
||||
inline bool VerifyResponseData(flatbuffers::Verifier &verifier, const void *union_obj, ResponseData type);
|
||||
|
||||
MANUALLY_ALIGNED_STRUCT(2) StructTest FLATBUFFERS_FINAL_CLASS {
|
||||
private:
|
||||
int16_t a_;
|
||||
int8_t b_;
|
||||
int8_t c_;
|
||||
|
||||
public:
|
||||
StructTest(int16_t a, int8_t b, Foo c)
|
||||
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), c_(flatbuffers::EndianScalar(static_cast<int8_t>(c))) { }
|
||||
|
||||
int16_t a() const { return flatbuffers::EndianScalar(a_); }
|
||||
int8_t b() const { return flatbuffers::EndianScalar(b_); }
|
||||
Foo c() const { return static_cast<Foo>(flatbuffers::EndianScalar(c_)); }
|
||||
};
|
||||
STRUCT_END(StructTest, 4);
|
||||
|
||||
struct TableTest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
const StructTest *st() const { return GetStruct<const StructTest *>(4); }
|
||||
const flatbuffers::Vector<uint8_t> *iv() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(6); }
|
||||
const flatbuffers::String *name() const { return GetPointer<const flatbuffers::String *>(8); }
|
||||
uint32_t id() const { return GetField<uint32_t>(10, 0); }
|
||||
bool KeyCompareLessThan(const TableTest *o) const { return id() < o->id(); }
|
||||
int KeyCompareWithValue(uint32_t val) const { return id() < val ? -1 : id() > val; }
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyField<StructTest>(verifier, 4 /* st */) &&
|
||||
VerifyField<flatbuffers::uoffset_t>(verifier, 6 /* iv */) &&
|
||||
verifier.Verify(iv()) &&
|
||||
VerifyFieldRequired<flatbuffers::uoffset_t>(verifier, 8 /* name */) &&
|
||||
verifier.Verify(name()) &&
|
||||
VerifyField<uint32_t>(verifier, 10 /* id */) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct TableTestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_st(const StructTest *st) { fbb_.AddStruct(4, st); }
|
||||
void add_iv(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> iv) { fbb_.AddOffset(6, iv); }
|
||||
void add_name(flatbuffers::Offset<flatbuffers::String> name) { fbb_.AddOffset(8, name); }
|
||||
void add_id(uint32_t id) { fbb_.AddElement<uint32_t>(10, id, 0); }
|
||||
TableTestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
TableTestBuilder &operator=(const TableTestBuilder &);
|
||||
flatbuffers::Offset<TableTest> Finish() {
|
||||
auto o = flatbuffers::Offset<TableTest>(fbb_.EndTable(start_, 4));
|
||||
fbb_.Required(o, 8); // name
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<TableTest> CreateTableTest(flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const StructTest *st = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> iv = 0,
|
||||
flatbuffers::Offset<flatbuffers::String> name = 0,
|
||||
uint32_t id = 0) {
|
||||
TableTestBuilder builder_(_fbb);
|
||||
builder_.add_id(id);
|
||||
builder_.add_name(name);
|
||||
builder_.add_iv(iv);
|
||||
builder_.add_st(st);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AttachRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AttachRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AttachRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AttachRequestBuilder &operator=(const AttachRequestBuilder &);
|
||||
flatbuffers::Offset<AttachRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<AttachRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AttachRequest> CreateAttachRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AttachRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AttachResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AttachResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AttachResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AttachResponseBuilder &operator=(const AttachResponseBuilder &);
|
||||
flatbuffers::Offset<AttachResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<AttachResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AttachResponse> CreateAttachResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AttachResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct Request FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
uint32_t id() const { return GetField<uint32_t>(4, 0); }
|
||||
RequestData request_data_type() const { return static_cast<RequestData>(GetField<uint8_t>(6, 0)); }
|
||||
const void *request_data() const { return GetPointer<const void *>(8); }
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyField<uint32_t>(verifier, 4 /* id */) &&
|
||||
VerifyField<uint8_t>(verifier, 6 /* request_data_type */) &&
|
||||
VerifyField<flatbuffers::uoffset_t>(verifier, 8 /* request_data */) &&
|
||||
VerifyRequestData(verifier, request_data(), request_data_type()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_id(uint32_t id) { fbb_.AddElement<uint32_t>(4, id, 0); }
|
||||
void add_request_data_type(RequestData request_data_type) { fbb_.AddElement<uint8_t>(6, static_cast<uint8_t>(request_data_type), 0); }
|
||||
void add_request_data(flatbuffers::Offset<void> request_data) { fbb_.AddOffset(8, request_data); }
|
||||
RequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
RequestBuilder &operator=(const RequestBuilder &);
|
||||
flatbuffers::Offset<Request> Finish() {
|
||||
auto o = flatbuffers::Offset<Request>(fbb_.EndTable(start_, 3));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Request> CreateRequest(flatbuffers::FlatBufferBuilder &_fbb,
|
||||
uint32_t id = 0,
|
||||
RequestData request_data_type = RequestData_NONE,
|
||||
flatbuffers::Offset<void> request_data = 0) {
|
||||
RequestBuilder builder_(_fbb);
|
||||
builder_.add_request_data(request_data);
|
||||
builder_.add_id(id);
|
||||
builder_.add_request_data_type(request_data_type);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct Response FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
uint32_t id() const { return GetField<uint32_t>(4, 0); }
|
||||
ResponseData response_data_type() const { return static_cast<ResponseData>(GetField<uint8_t>(6, 0)); }
|
||||
const void *response_data() const { return GetPointer<const void *>(8); }
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyField<uint32_t>(verifier, 4 /* id */) &&
|
||||
VerifyField<uint8_t>(verifier, 6 /* response_data_type */) &&
|
||||
VerifyField<flatbuffers::uoffset_t>(verifier, 8 /* response_data */) &&
|
||||
VerifyResponseData(verifier, response_data(), response_data_type()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_id(uint32_t id) { fbb_.AddElement<uint32_t>(4, id, 0); }
|
||||
void add_response_data_type(ResponseData response_data_type) { fbb_.AddElement<uint8_t>(6, static_cast<uint8_t>(response_data_type), 0); }
|
||||
void add_response_data(flatbuffers::Offset<void> response_data) { fbb_.AddOffset(8, response_data); }
|
||||
ResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
ResponseBuilder &operator=(const ResponseBuilder &);
|
||||
flatbuffers::Offset<Response> Finish() {
|
||||
auto o = flatbuffers::Offset<Response>(fbb_.EndTable(start_, 3));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Response> CreateResponse(flatbuffers::FlatBufferBuilder &_fbb,
|
||||
uint32_t id = 0,
|
||||
ResponseData response_data_type = ResponseData_NONE,
|
||||
flatbuffers::Offset<void> response_data = 0) {
|
||||
ResponseBuilder builder_(_fbb);
|
||||
builder_.add_response_data(response_data);
|
||||
builder_.add_id(id);
|
||||
builder_.add_response_data_type(response_data_type);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline bool VerifyRequestData(flatbuffers::Verifier &verifier, const void *union_obj, RequestData type) {
|
||||
switch (type) {
|
||||
case RequestData_NONE: return true;
|
||||
case RequestData_AttachRequest: return verifier.VerifyTable(reinterpret_cast<const AttachRequest *>(union_obj));
|
||||
case RequestData_ListBreakpointsRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::ListBreakpointsRequest *>(union_obj));
|
||||
case RequestData_AddBreakpointRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::AddBreakpointRequest *>(union_obj));
|
||||
case RequestData_UpdateBreakpointRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::UpdateBreakpointRequest *>(union_obj));
|
||||
case RequestData_RemoveBreakpointRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::RemoveBreakpointRequest *>(union_obj));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool VerifyResponseData(flatbuffers::Verifier &verifier, const void *union_obj, ResponseData type) {
|
||||
switch (type) {
|
||||
case ResponseData_NONE: return true;
|
||||
case ResponseData_AttachResponse: return verifier.VerifyTable(reinterpret_cast<const AttachResponse *>(union_obj));
|
||||
case ResponseData_ListBreakpointsResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::ListBreakpointsResponse *>(union_obj));
|
||||
case ResponseData_AddBreakpointResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::AddBreakpointResponse *>(union_obj));
|
||||
case ResponseData_UpdateBreakpointResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::UpdateBreakpointResponse *>(union_obj));
|
||||
case ResponseData_RemoveBreakpointResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::RemoveBreakpointResponse *>(union_obj));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace proto
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_MESSAGES_XE_DEBUG_PROTO_H_
|
||||
@@ -1,27 +0,0 @@
|
||||
namespace Xenia.Debugger.Proto;
|
||||
|
||||
attribute "priority"; // ?
|
||||
|
||||
enum Foo:byte (bit_flags) {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
struct StructTest {
|
||||
a:short;
|
||||
b:byte;
|
||||
c:Foo;
|
||||
}
|
||||
|
||||
table TableTest {
|
||||
st:StructTest;
|
||||
iv:[ubyte];
|
||||
name:string (required);
|
||||
id:uint (required, key);
|
||||
}
|
||||
|
||||
union UnionTest {
|
||||
TableTest,
|
||||
}
|
||||
|
||||
root_type UnionTest;
|
||||
Reference in New Issue
Block a user