2013-12-21 20:53:49 +01:00
|
|
|
/**
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
|
|
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
|
|
|
******************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
var module = angular.module('xe.datasources', []);
|
|
|
|
|
|
|
|
|
|
|
2013-12-23 00:42:41 +01:00
|
|
|
module.service('Breakpoint', function() {
|
|
|
|
|
// http://stackoverflow.com/a/2117523/377392
|
|
|
|
|
var uuidFormat = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
|
|
|
|
|
function uuid4() {
|
|
|
|
|
return uuidFormat.replace(/[xy]/g, function(c) {
|
|
|
|
|
var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
|
|
|
|
|
return v.toString(16);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var Breakpoint = function(opt_id) {
|
|
|
|
|
this.id = opt_id || uuid4();
|
|
|
|
|
this.type = Breakpoint.Type.TEMP;
|
|
|
|
|
this.address = 0;
|
|
|
|
|
this.enabled = true;
|
|
|
|
|
};
|
|
|
|
|
Breakpoint.Type = {
|
|
|
|
|
TEMP: 'temp',
|
|
|
|
|
CODE: 'code'
|
|
|
|
|
};
|
|
|
|
|
Breakpoint.fromJSON = function(json) {
|
|
|
|
|
var breakpoint = new Breakpoint(json.id);
|
|
|
|
|
breakpoint.type = json.type;
|
|
|
|
|
breakpoint.address = json.address;
|
|
|
|
|
breakpoint.enabled = json.enabled;
|
|
|
|
|
return breakpoint;
|
|
|
|
|
};
|
|
|
|
|
Breakpoint.prototype.toJSON = function() {
|
|
|
|
|
return {
|
|
|
|
|
'id': this.id,
|
|
|
|
|
'type': this.type,
|
|
|
|
|
'address': this.address,
|
|
|
|
|
'enabled': this.enabled
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
return Breakpoint;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2013-12-22 09:21:35 +01:00
|
|
|
module.service('DataSource', function($q) {
|
|
|
|
|
var DataSource = function(source) {
|
|
|
|
|
this.source = source;
|
|
|
|
|
this.online = false;
|
|
|
|
|
this.status = 'disconnected';
|
|
|
|
|
};
|
|
|
|
|
DataSource.prototype.open = function() {};
|
|
|
|
|
DataSource.prototype.dispose = function() {};
|
|
|
|
|
DataSource.prototype.issue = function(command) {};
|
|
|
|
|
|
2013-12-23 01:48:50 +01:00
|
|
|
DataSource.prototype.makeReady = function() {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'debug.make_ready'
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DataSource.prototype.getModuleList = function() {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.get_module_list'
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DataSource.prototype.getModule = function(moduleName) {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.get_module',
|
|
|
|
|
module: moduleName
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DataSource.prototype.getFunctionList = function(moduleName) {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.get_function_list',
|
|
|
|
|
module: moduleName
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DataSource.prototype.getFunction = function(address) {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.get_function',
|
|
|
|
|
address: address
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2013-12-22 09:21:35 +01:00
|
|
|
DataSource.prototype.addBreakpoint = function(breakpoint) {
|
|
|
|
|
return this.addBreakpoints([breakpoint]);
|
|
|
|
|
};
|
2013-12-21 20:53:49 +01:00
|
|
|
|
2013-12-22 09:21:35 +01:00
|
|
|
DataSource.prototype.addBreakpoints = function(breakpoints) {
|
|
|
|
|
if (!breakpoints.length) {
|
|
|
|
|
var d = $q.defer();
|
|
|
|
|
d.resolve();
|
|
|
|
|
return d.promise;
|
|
|
|
|
}
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.add_breakpoints',
|
|
|
|
|
breakpoints: breakpoints.map(function(breakpoint) {
|
|
|
|
|
return breakpoint.toJSON();
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
};
|
2013-12-21 20:53:49 +01:00
|
|
|
|
2013-12-22 09:21:35 +01:00
|
|
|
DataSource.prototype.removeBreakpoint = function(breakpointId) {
|
|
|
|
|
return this.removeBreakpoints([breakpointId]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DataSource.prototype.removeBreakpoints = function(breakpointIds) {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.remove_breakpoints',
|
|
|
|
|
breakpointIds: breakpointIds
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DataSource.prototype.removeAllBreakpoints = function() {
|
|
|
|
|
return this.issue({
|
|
|
|
|
command: 'cpu.remove_all_breakpoints'
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return DataSource;
|
|
|
|
|
});
|
|
|
|
|
|
2013-12-22 18:25:44 +01:00
|
|
|
module.service('RemoteDataSource', function($q, log, DataSource) {
|
2013-12-21 20:53:49 +01:00
|
|
|
var RemoteDataSource = function(url) {
|
|
|
|
|
DataSource.call(this, url);
|
|
|
|
|
this.url = url;
|
|
|
|
|
this.socket = null;
|
2013-12-22 09:21:35 +01:00
|
|
|
this.nextRequestId_ = 1;
|
|
|
|
|
this.pendingRequests_ = {};
|
2013-12-21 20:53:49 +01:00
|
|
|
};
|
|
|
|
|
inherits(RemoteDataSource, DataSource);
|
|
|
|
|
|
|
|
|
|
RemoteDataSource.prototype.open = function() {
|
|
|
|
|
var url = this.url;
|
|
|
|
|
|
|
|
|
|
this.online = false;
|
|
|
|
|
this.status = 'connecting';
|
|
|
|
|
|
|
|
|
|
var d = $q.defer();
|
|
|
|
|
|
|
|
|
|
this.socket = new WebSocket(url, []);
|
|
|
|
|
this.socket.onopen = (function() {
|
|
|
|
|
// TODO(benvanik): handshake
|
|
|
|
|
|
|
|
|
|
this.online = true;
|
|
|
|
|
this.status = 'connected';
|
|
|
|
|
d.resolve();
|
|
|
|
|
}).bind(this);
|
|
|
|
|
|
|
|
|
|
this.socket.onclose = (function(e) {
|
|
|
|
|
this.online = false;
|
|
|
|
|
if (this.status == 'connecting') {
|
|
|
|
|
this.status = 'disconnected';
|
|
|
|
|
d.reject(e.code + ' ' + e.reason);
|
|
|
|
|
} else {
|
|
|
|
|
this.status = 'disconnected';
|
2013-12-22 18:25:44 +01:00
|
|
|
log.info('Disconnected');
|
2013-12-21 20:53:49 +01:00
|
|
|
}
|
|
|
|
|
}).bind(this);
|
|
|
|
|
|
|
|
|
|
this.socket.onerror = (function(e) {
|
|
|
|
|
// ?
|
|
|
|
|
}).bind(this);
|
|
|
|
|
|
|
|
|
|
this.socket.onmessage = (function(e) {
|
|
|
|
|
console.log('message', e.data);
|
2013-12-22 09:21:35 +01:00
|
|
|
var json = JSON.parse(e.data);
|
|
|
|
|
if (json.requestId) {
|
|
|
|
|
// Response to a previous request.
|
|
|
|
|
var request = this.pendingRequests_[json.requestId];
|
|
|
|
|
if (request) {
|
|
|
|
|
delete this.pendingRequests_[json.requestId];
|
|
|
|
|
if (json.status) {
|
|
|
|
|
request.deferred.resolve(json.result);
|
|
|
|
|
} else {
|
|
|
|
|
request.deferred.reject(json.result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Notification.
|
|
|
|
|
}
|
2013-12-21 20:53:49 +01:00
|
|
|
}).bind(this);
|
|
|
|
|
|
|
|
|
|
return d.promise;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
RemoteDataSource.prototype.dispose = function() {
|
2013-12-22 09:21:35 +01:00
|
|
|
this.pendingRequests_ = {};
|
2013-12-21 20:53:49 +01:00
|
|
|
this.online = false;
|
|
|
|
|
this.status = 'disconnected';
|
|
|
|
|
if (this.socket) {
|
|
|
|
|
this.socket.close();
|
|
|
|
|
this.socket = null;
|
|
|
|
|
}
|
|
|
|
|
DataSource.prototype.dispose.call(this);
|
|
|
|
|
};
|
|
|
|
|
|
2013-12-22 09:21:35 +01:00
|
|
|
RemoteDataSource.prototype.issue = function(command) {
|
|
|
|
|
var d = $q.defer();
|
|
|
|
|
command.requestId = this.nextRequestId_++;
|
|
|
|
|
this.socket.send(JSON.stringify(command));
|
|
|
|
|
command.deferred = d;
|
|
|
|
|
this.pendingRequests_[command.requestId] = command;
|
|
|
|
|
return d.promise;
|
|
|
|
|
};
|
|
|
|
|
|
2013-12-21 20:53:49 +01:00
|
|
|
return RemoteDataSource;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2013-12-22 09:21:35 +01:00
|
|
|
module.service('FileDataSource', function($q, DataSource) {
|
2013-12-21 20:53:49 +01:00
|
|
|
var FileDataSource = function(file) {
|
|
|
|
|
DataSource.call(this, file.name);
|
|
|
|
|
this.file = file;
|
|
|
|
|
};
|
|
|
|
|
inherits(FileDataSource, DataSource);
|
|
|
|
|
|
|
|
|
|
FileDataSource.prototype.open = function() {
|
|
|
|
|
this.status = 'connecting';
|
|
|
|
|
|
|
|
|
|
var d = $q.defer();
|
|
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
window.setTimeout(function() {
|
|
|
|
|
$scope.$apply((function() {
|
|
|
|
|
// TODO(benvanik): scan/load trace
|
|
|
|
|
|
|
|
|
|
this.online = true;
|
|
|
|
|
this.status = 'connected';
|
|
|
|
|
d.resolve();
|
|
|
|
|
}).bind(self));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return d.promise;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FileDataSource.prototype.dispose = function() {
|
|
|
|
|
this.online = false;
|
|
|
|
|
if (this.file) {
|
|
|
|
|
if (this.file.close) {
|
|
|
|
|
this.file.close();
|
|
|
|
|
}
|
|
|
|
|
this.file = null;
|
|
|
|
|
}
|
|
|
|
|
DataSource.prototype.dispose.call(this);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return FileDataSource;
|
|
|
|
|
});
|