cartridgeloader commentary and documenation

removed all references to hotloading. will reimplement in the future

made sure then cartridgeloader.Loader instances are closed when no
longer needed. added commentary where required to explain

information pane in the ROM selector window is not disabled when
animation emulation is not running. the load button is still visible
based on the animation emulation
This commit is contained in:
JetSetIlly 2024-04-16 19:42:20 +01:00
parent e3f4a743b6
commit 28ae543e36
9 changed files with 217 additions and 372 deletions

View file

@ -13,37 +13,16 @@
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
// Package cartridgeloader is used to specify the cartridge data that is to be
// attached to the emulated VCS.
//
// When the cartridge is ready to be loaded into the emulator, the Load()
// function should be used. The Load() function handles loading of data from a
// different sources. Currently on local-file and data over HTTP is supported.
//
// As well as the filename, the Loader type allows the cartridge mapping to be
// specified, if required. The simplest instantiation therefore is:
//
// cl := cartridgeloader.Loader{
// Filename: "roms/Pitfall.bin",
// }
//
// It is stronly preferred however, that the NewLoader() function is used to
// initialise the Loader or important fields risk being initialised
// incorrectly.
//
// The NewLoader() function accepts two arguments. The filename of the
// cartridge (which might be a HTTP url) and the cartridge mapper. In most
// cases the mapper will be "AUTO" to indicate that we don't know (or
// particular care) what the mapping format is.
// Package cartridgeloader is used to load cartridge data so that it can be used
// with the cartridge pacakage
//
// # File Extensions
//
// The file extension of a file will specify the cartridge mapping and will
// cause the emulation to use that mapping. Most 2600 ROM files have the
// extension "bin" but sometimes it is necessary to specify explicitly what
// the mapper is.
// cause the emulation to use that mapping.
//
// The following quoted file extensions are recognised (case insenstive):
// The following file extensions are recognised and force the use of the
// specified mapper:
//
// Atari 2k "2k"
// Atari 4k "4k"
@ -59,7 +38,7 @@
// Parker Bros "E0"
// M-Network "E7"
// Tigervision "3F"
// Supercharger "AR"
// Supercharger "AR", "MP3, "WAV"
// DF "DF"
// 3E "3E"
// 3E+ "3E+"
@ -67,64 +46,18 @@
// DPC (Pitfall2) "DPC"
// DPC+ "DP+"
// CDF "CDF" (including CDFJ)
// MovieCart "MVC"
//
// In addition to these above there a three "special" mapping formats. Which
// aren't really mappers in the normal sense but which will none-the-less
// trigger a particular cartridge emulation path.
// File extensions are case insensitive.
//
// "MP3" and "WAV" indicate that the emulation should use the Supercharger
// mapper but with the understanding that the data will be loaded from audio
// data.
// A file extension of "BIN", "ROM", "A26" indicates that the data should be
// fingerprinted as normal.
//
// "MVC" indicates that the data is a MovieCart stream. Because of the
// potential size of these files, data is streamed from the file. Also,
// steaming over HTTP is not yet currently supported.
// # Hashes
//
// Finally a file extension of "BIN", "ROM", "A26" will tell the mapping system
// to "fingerprint" the cartridge data to ascertain the mapping type.
//
// Fingerprinting is not handled by the cartridlgeloader package.
//
// # Hash
//
// The Hash field of the Loader type contains the SHA1 value of the loaded
// data. It is valid after the Load() function has completed successfully. If
// the field is not empty before Load() is called, that value will be compared
// with the calculated value. An error is returned if the values differ.
//
// In most cases you wouldn't want to change the Hash field before calling the
// Load() function but it is sometimes useful to make sure the correct file is
// being loaded - for example the recorder package uses it to help make sure
// playback is correct.
//
// The hash value is invalid/unused in the case of streamed data
//
// # Streaming
//
// For some cartridge types it is necessary to stream bytes from the file
// rather than load them all at once. For these types of cartridges the Load()
// function will open a stream and readbable via the StreamedData field.
//
// The function IsStreaming() returns true if data is to be read from the
// StreamedData field rather than the Data field.
//
// For streaming to work NewLoader() must have been used to instantiate the
// Loader type.
//
// # Closing
//
// Instances of Loader must be closed with Close() when it is no longer
// required.
//
// # OnInserted
//
// The OnInserted field is used by some cartridges to indicate when the
// cartridge data has been loaded into memory and something else needs to
// happen that is outside of the scope of the cartridge package. Currently,
// this is used by the Supercharder and PlusROM.
//
// # Notification Hook
//
// Some cartridge mappers will generate notifications (see notification.Notify type). The
// NotifcationHook can be specified in order to respond to those events.
// Creating a cartridge loader with NewLoaderFromFilename() or
// NewLoaderFromData() will also create a SHA1 and MD5 hash of the data. The
// amount of data used to create the has is limited to 1MB. For most cartridges
// this will mean the hash is taken using all the data but some cartridge are
// likely to have much more data than that.
package cartridgeloader

View file

@ -34,6 +34,8 @@ import (
// the maximum amount of data to load into the peep slice
const maxPeepLength = 1048576
// makes sures that data is capped at peep length. use this function when
// assigning to the Loader.peep field
func peepData(data []byte) []byte {
return data[:min(len(data), maxPeepLength)]
}
@ -74,8 +76,8 @@ type Loader struct {
// help fingerprinting and for creating the SHA1 and MD5 hashes
//
// in reality, most cartridges are small enough to fit entirely inside the
// peep slice. currently it is only moviecart data and maybe supercharger
// sound files that will be larger than the maxPeepLength
// peep field. currently it is only moviecart data and supercharger sound
// files that are ever arger than maxPeepLength
peep []byte
// data was supplied through NewLoaderFromData()
@ -256,19 +258,19 @@ func (ld Loader) Size() int {
return ld.size
}
// Contains returns true if subslice appears anywhere in the data
// Contains returns true if subslice appears anywhere in the peep data
func (ld Loader) Contains(subslice []byte) bool {
return bytes.Contains(ld.peep, subslice)
}
// ContainsLimit returns true if subslice appears in the data at an offset between
// ContainsLimit returns true if subslice appears in the peep data at an offset between
// zero and limit
func (ld Loader) ContainsLimit(limit int, subslice []byte) bool {
limit = min(limit, ld.Size())
return bytes.Contains(ld.peep[:limit], subslice)
}
// Count returns the number of non-overlapping instances of subslice in the data
// Count returns the number of non-overlapping instances of subslice in the peep data
func (ld Loader) Count(subslice []byte) int {
return bytes.Count(ld.peep, subslice)
}

View file

@ -177,6 +177,7 @@ func (cmp *Comparison) CreateFromLoader(cartload cartridgeloader.Loader) error {
defer func() {
cmp.driver.quit <- nil
cmp.isEmulating.Store(false)
cartload.Close()
}()
// not using setup system to attach cartridge. maybe we should?

View file

@ -25,7 +25,6 @@ import (
"strconv"
"strings"
"github.com/jetsetilly/gopher2600/cartridgeloader"
"github.com/jetsetilly/gopher2600/coprocessor"
coproc_breakpoints "github.com/jetsetilly/gopher2600/coprocessor/developer/breakpoints"
"github.com/jetsetilly/gopher2600/coprocessor/developer/callstack"
@ -472,21 +471,11 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
case cmdInsert:
dbg.unwindLoop(func() error {
cart, _ := tokens.Get()
cl, err := cartridgeloader.NewLoaderFromFilename(cart, "AUTO")
filename, _ := tokens.Get()
err := dbg.insertCartridge(filename)
if err != nil {
return err
}
err = dbg.attachCartridge(cl)
if err != nil {
return err
}
// use cartridge's idea of the filename. the attach process may have
// caused a different cartridge to load than the one requested (most
// typically this will mean that the cartridge has been ejected)
dbg.printLine(terminal.StyleFeedback, "machine reset with new cartridge (%s)", dbg.vcs.Mem.Cart.Filename)
return nil
})
@ -573,14 +562,6 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
dbg.printLine(terminal.StyleFeedback, "cartridge has no RAM")
}
case "HOTLOAD":
err := dbg.hotload()
if err != nil {
dbg.printLine(terminal.StyleFeedback, err.Error())
} else {
dbg.printLine(terminal.StyleFeedback, "hotload successful")
}
case "DUMP":
romdump, err := dbg.vcs.Mem.Cart.ROMDump()
if err != nil {
@ -1496,7 +1477,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
case "DUMP":
dump := func(name string) {
if data, ok := static.Reference(name); ok {
fn := unique.Filename(fmt.Sprintf("dump_%s", name), dbg.loader.Name)
fn := unique.Filename(fmt.Sprintf("dump_%s", name), dbg.cartload.Name)
fn = fmt.Sprintf("%s.bin", fn)
err := os.WriteFile(fn, data, 0644)
if err != nil {
@ -1799,7 +1780,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
if ok {
switch strings.ToUpper(controller) {
case "AUTO":
dbg.vcs.FingerprintPeripheral(id, *dbg.loader)
dbg.vcs.FingerprintPeripheral(id, *dbg.cartload)
case "STICK":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewStick)
case "PADDLE":

View file

@ -96,7 +96,7 @@ var commandTemplate = []string{
cmdGoto + " [%<clock>N] (%<scanline>N) (%<frame>N)",
cmdInsert + " %<cartridge>F",
cmdCartridge + " (PATH|NAME|MAPPER|CONTAINER|MAPPEDBANKS|HASH|STATIC|REGISTERS|RAM|HOTLOAD|DUMP)",
cmdCartridge + " (PATH|NAME|MAPPER|CONTAINER|MAPPEDBANKS|HASH|STATIC|REGISTERS|RAM|DUMP)",
cmdPatch + " %<patch file>S",
cmdDisasm + " (BYTECODE|REDUX)",
cmdGrep + " (OPERATOR|OPERAND|COPROC) %<search>S",

View file

@ -99,9 +99,8 @@ type Debugger struct {
// components may change (during rewind for example)
vcs *hardware.VCS
// the last loader to be used. we keep a reference to it so we can make
// sure Close() is called on end
loader *cartridgeloader.Loader
// keep a reference to the current cartridgeloader to make sure Close() is called
cartload *cartridgeloader.Loader
// preview emulation is used to gather information about a ROM before
// running it fully
@ -707,6 +706,8 @@ func (dbg *Debugger) StartInDebugMode(filename string) error {
}
}
// cartload is should be passed to attachCartridge() almost immediately. the
// closure of cartload will then be handled for us
err = dbg.attachCartridge(cartload)
if err != nil {
return fmt.Errorf("debugger: %w", err)
@ -778,24 +779,25 @@ func (dbg *Debugger) StartInPlayMode(filename string) error {
// set running flag as early as possible
dbg.running = true
var err error
var cartload cartridgeloader.Loader
if filename == "" {
cartload = cartridgeloader.Loader{}
} else {
cartload, err = cartridgeloader.NewLoaderFromFilename(filename, dbg.opts.Mapping)
if err != nil {
return fmt.Errorf("debugger: %w", err)
}
}
err = recorder.IsPlaybackFile(filename)
err := recorder.IsPlaybackFile(filename)
if err != nil {
if !errors.Is(err, recorder.NotAPlaybackFile) {
return fmt.Errorf("debugger: %w", err)
}
var cartload cartridgeloader.Loader
if filename == "" {
cartload = cartridgeloader.Loader{}
} else {
cartload, err = cartridgeloader.NewLoaderFromFilename(filename, dbg.opts.Mapping)
if err != nil {
return fmt.Errorf("debugger: %w", err)
}
}
// cartload is should be passed to attachCartridge() almost immediately. the
// closure of cartload will then be handled for us
err = dbg.attachCartridge(cartload)
if err != nil {
return fmt.Errorf("debugger: %w", err)
@ -940,8 +942,8 @@ func (dbg *Debugger) run() error {
// make sure any cartridge loader has been finished with
defer func() {
if dbg.loader != nil {
err := dbg.loader.Close()
if dbg.cartload != nil {
err := dbg.cartload.Close()
if err != nil {
logger.Logf("debugger", err.Error())
}
@ -1141,9 +1143,12 @@ func (dbg *Debugger) Notify(notice notifications.Notice) error {
//
// if the new cartridge loader has the same filename as the previous loader
// then reset() is called with a newCartridge argument of false.
//
// VERY IMPORTANT that the supplied cartload is assigned to dbg.cartload before
// returning from the function
func (dbg *Debugger) attachCartridge(cartload cartridgeloader.Loader) (e error) {
// is this a new cartridge we're loading. value is used for dbg.reset()
newCartridge := dbg.loader == nil || cartload.Filename != dbg.loader.Filename
newCartridge := dbg.cartload == nil || cartload.Filename != dbg.cartload.Filename
// stop optional sub-systems that shouldn't survive a new cartridge insertion
dbg.endPlayback()
@ -1169,13 +1174,13 @@ func (dbg *Debugger) attachCartridge(cartload cartridgeloader.Loader) (e error)
}()
// close any existing loader before continuing
if dbg.loader != nil {
err := dbg.loader.Close()
if dbg.cartload != nil {
err := dbg.cartload.Close()
if err != nil {
return err
logger.Logf("debuger", err.Error())
}
}
dbg.loader = &cartload
dbg.cartload = &cartload
// reset of vcs is implied with attach cartridge
err := setup.AttachCartridge(dbg.vcs, cartload, false)
@ -1332,11 +1337,14 @@ func (dbg *Debugger) startComparison(comparisonROM string, comparisonPrefs strin
return err
}
// cartload is passed to comparision.CreateFromLoader(). closure will be
// handled from there
cartload, err := cartridgeloader.NewLoaderFromFilename(comparisonROM, "AUTO")
if err != nil {
return err
}
// comparision emulation handles closure of cartridgeloader
dbg.comparison.CreateFromLoader(cartload)
// check use of comparison prefs
@ -1361,49 +1369,6 @@ func (dbg *Debugger) endComparison() {
}
}
func (dbg *Debugger) hotload() (e error) {
// tell GUI that we're in the initialistion phase
dbg.setState(govern.Initialising, govern.Normal)
defer func() {
if dbg.runUntilHalt && e == nil {
dbg.setState(govern.Running, govern.Normal)
} else {
dbg.setState(govern.Paused, govern.Normal)
}
}()
// close any existing loader before continuing
if dbg.loader != nil {
err := dbg.loader.Close()
if err != nil {
return err
}
}
cartload, err := cartridgeloader.NewLoaderFromFilename(dbg.vcs.Mem.Cart.Filename, dbg.vcs.Mem.Cart.ID())
if err != nil {
return err
}
err = dbg.vcs.Mem.Cart.HotLoad(cartload)
if err != nil {
return err
}
dbg.loader = &cartload
// disassemble newly attached cartridge
err = dbg.Disasm.FromMemory()
if err != nil {
return err
}
dbg.CoProcDisasm.AttachCartridge(dbg)
dbg.CoProcDev.AttachCartridge(dbg, cartload.Filename, dbg.opts.ELF)
return nil
}
// parseInput splits the input into individual commands. each command is then
// passed to parseCommand for processing
//
@ -1453,7 +1418,7 @@ func (dbg *Debugger) Plugged(port plugging.PortID, peripheral plugging.Periphera
}
func (dbg *Debugger) reloadCartridge() error {
if dbg.loader == nil {
if dbg.cartload == nil {
return nil
}
@ -1462,7 +1427,7 @@ func (dbg *Debugger) reloadCartridge() error {
dbg.macro.Reset()
}
return dbg.insertCartridge(dbg.loader.Filename)
return dbg.insertCartridge(dbg.cartload.Filename)
}
// ReloadCartridge inserts the current cartridge and states the emulation over.
@ -1472,7 +1437,7 @@ func (dbg *Debugger) ReloadCartridge() {
func (dbg *Debugger) insertCartridge(filename string) error {
if filename == "" {
filename = dbg.loader.Filename
filename = dbg.cartload.Filename
}
cartload, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO")
@ -1480,6 +1445,8 @@ func (dbg *Debugger) insertCartridge(filename string) error {
return fmt.Errorf("debugger: %w", err)
}
// cartload is should be passed to attachCartridge() almost immediately. the
// closure of cartload will then be handled for us
err = dbg.attachCartridge(cartload)
if err != nil {
return fmt.Errorf("debugger: %w", err)

View file

@ -396,187 +396,172 @@ func (win *winSelectROM) draw() {
// control buttons. start controlHeight measurement
win.controlHeight = imguiMeasureHeight(func() {
func() {
if !win.thmb.IsEmulating() {
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
defer imgui.PopItemFlag()
defer imgui.PopStyleVar()
}
imgui.SetNextItemOpen(win.informationOpen, imgui.ConditionAlways)
if !imgui.CollapsingHeaderV(win.selectedName, imgui.TreeNodeFlagsNone) {
win.informationOpen = false
} else {
win.informationOpen = true
if imgui.BeginTable("#properties", 3) {
imgui.TableSetupColumnV("#information", imgui.TableColumnFlagsWidthStretch, -1, 0)
imgui.TableSetupColumnV("#spacingA", imgui.TableColumnFlagsWidthFixed, -1, 1)
imgui.TableSetupColumnV("#boxart", imgui.TableColumnFlagsWidthFixed, -1, 2)
imgui.SetNextItemOpen(win.informationOpen, imgui.ConditionAlways)
if !imgui.CollapsingHeaderV(win.selectedName, imgui.TreeNodeFlagsNone) {
win.informationOpen = false
} else {
win.informationOpen = true
if imgui.BeginTable("#properties", 3) {
imgui.TableSetupColumnV("#information", imgui.TableColumnFlagsWidthStretch, -1, 0)
imgui.TableSetupColumnV("#spacingA", imgui.TableColumnFlagsWidthFixed, -1, 1)
imgui.TableSetupColumnV("#boxart", imgui.TableColumnFlagsWidthFixed, -1, 2)
// property table. we measure the height of this table to
// help centering the box art image in the next column
imgui.TableNextRow()
imgui.TableNextColumn()
propertyTableTop := imgui.CursorPosY()
if imgui.BeginTable("#properties", 2) {
imgui.TableSetupColumnV("#category", imgui.TableColumnFlagsWidthFixed, -1, 0)
imgui.TableSetupColumnV("#detail", imgui.TableColumnFlagsWidthFixed, -1, 1)
// wrap text
imgui.PushTextWrapPosV(imgui.CursorPosX() + imgui.ContentRegionAvail().X)
defer imgui.PopTextWrapPos()
// property table. we measure the height of this table to
// help centering the box art image in the next column
imgui.TableNextRow()
imgui.TableNextColumn()
propertyTableTop := imgui.CursorPosY()
if imgui.BeginTable("#properties", 2) {
imgui.TableSetupColumnV("#category", imgui.TableColumnFlagsWidthFixed, -1, 0)
imgui.TableSetupColumnV("#detail", imgui.TableColumnFlagsWidthFixed, -1, 1)
// wrap text
imgui.PushTextWrapPosV(imgui.CursorPosX() + imgui.ContentRegionAvail().X)
defer imgui.PopTextWrapPos()
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.Text("Name")
imgui.TableNextColumn()
if win.selectedProperties.IsValid() {
imgui.Text(win.selectedProperties.Name)
} else {
imgui.Text(win.selectedName)
}
// results of preview emulation from the thumbnailer
selectedFilePreview := win.thmb.PreviewResults()
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Mapper")
imgui.TableNextColumn()
if selectedFilePreview != nil {
imgui.Text(selectedFilePreview.VCS.Mem.Cart.ID())
}
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
imgui.AlignTextToFramePadding()
imgui.Text("Television")
imgui.TableNextColumn()
if selectedFilePreview != nil {
imgui.SetNextItemWidth(80)
if imgui.BeginCombo("##tvspec", selectedFilePreview.FrameInfo.Spec.ID) {
for _, s := range specification.SpecList {
if imgui.Selectable(s) {
}
}
imgui.EndCombo()
}
}
imgui.PopStyleVar()
imgui.PopItemFlag()
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
imgui.AlignTextToFramePadding()
imgui.Text("Players")
imgui.TableNextColumn()
if selectedFilePreview != nil {
imgui.SetNextItemWidth(100)
if imgui.BeginCombo("##leftplayer", string(selectedFilePreview.VCS.RIOT.Ports.LeftPlayer.ID())) {
for _, s := range peripherals.AvailableLeftPlayer {
if imgui.Selectable(s) {
}
}
imgui.EndCombo()
}
imgui.SameLineV(0, 15)
imgui.Text("&")
imgui.SameLineV(0, 15)
imgui.SetNextItemWidth(100)
if imgui.BeginCombo("##rightplayer", string(selectedFilePreview.VCS.RIOT.Ports.RightPlayer.ID())) {
for _, s := range peripherals.AvailableRightPlayer {
if imgui.Selectable(s) {
}
}
imgui.EndCombo()
}
}
imgui.PopStyleVar()
imgui.PopItemFlag()
if win.selectedProperties.Manufacturer != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Manufacturer")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Manufacturer)
}
if win.selectedProperties.Rarity != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Rarity")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Rarity)
}
if win.selectedProperties.Model != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Model")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Model)
}
if win.selectedProperties.Note != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Note")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Note)
}
imgui.EndTable()
}
propertyTableBottom := imgui.CursorPosY()
propertyTableHeight := propertyTableBottom - propertyTableTop
// spacing
imgui.Text("Name")
imgui.TableNextColumn()
if win.selectedProperties.IsValid() {
imgui.Text(win.selectedProperties.Name)
} else {
imgui.Text(win.selectedName)
}
if win.boxartUse {
imgui.TableNextColumn()
sz := imgui.Vec2{float32(win.boxartDimensions.X), float32(win.boxartDimensions.Y)}
// results of preview emulation from the thumbnailer
selectedFilePreview := win.thmb.PreviewResults()
// if thumbnail height is less than height of
// property table then we position the image so that
// it's centered in relation to the property table
p := imgui.CursorPos()
if sz.Y < propertyTableHeight {
p.Y += (propertyTableHeight - sz.Y) / 2
imgui.SetCursorPos(p)
} else {
// if height of thumbnail is greater than or
// equal to height of property table then we add
// a imgui.Spacing(). this may expand the height
// of the property table but that's okay
imgui.Spacing()
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Mapper")
imgui.TableNextColumn()
if selectedFilePreview != nil {
imgui.Text(selectedFilePreview.VCS.Mem.Cart.ID())
}
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
imgui.AlignTextToFramePadding()
imgui.Text("Television")
imgui.TableNextColumn()
if selectedFilePreview != nil {
imgui.SetNextItemWidth(80)
if imgui.BeginCombo("##tvspec", selectedFilePreview.FrameInfo.Spec.ID) {
for _, s := range specification.SpecList {
if imgui.Selectable(s) {
}
}
imgui.EndCombo()
}
}
imgui.PopStyleVar()
imgui.PopItemFlag()
imgui.Image(imgui.TextureID(win.boxartTexture.getID()), sz)
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha)
imgui.AlignTextToFramePadding()
imgui.Text("Players")
imgui.TableNextColumn()
if selectedFilePreview != nil {
imgui.SetNextItemWidth(100)
if imgui.BeginCombo("##leftplayer", string(selectedFilePreview.VCS.RIOT.Ports.LeftPlayer.ID())) {
for _, s := range peripherals.AvailableLeftPlayer {
if imgui.Selectable(s) {
}
}
imgui.EndCombo()
}
imgui.SameLineV(0, 15)
imgui.Text("&")
imgui.SameLineV(0, 15)
imgui.SetNextItemWidth(100)
if imgui.BeginCombo("##rightplayer", string(selectedFilePreview.VCS.RIOT.Ports.RightPlayer.ID())) {
for _, s := range peripherals.AvailableRightPlayer {
if imgui.Selectable(s) {
}
}
imgui.EndCombo()
}
}
imgui.PopStyleVar()
imgui.PopItemFlag()
if win.selectedProperties.Manufacturer != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Manufacturer")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Manufacturer)
}
if win.selectedProperties.Rarity != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Rarity")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Rarity)
}
if win.selectedProperties.Model != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Model")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Model)
}
if win.selectedProperties.Note != "" {
imgui.TableNextRow()
imgui.TableNextColumn()
imgui.AlignTextToFramePadding()
imgui.Text("Note")
imgui.TableNextColumn()
imgui.Text(win.selectedProperties.Note)
}
imgui.EndTable()
}
propertyTableBottom := imgui.CursorPosY()
propertyTableHeight := propertyTableBottom - propertyTableTop
// spacing
imgui.TableNextColumn()
if win.boxartUse {
imgui.TableNextColumn()
sz := imgui.Vec2{float32(win.boxartDimensions.X), float32(win.boxartDimensions.Y)}
// if thumbnail height is less than height of
// property table then we position the image so that
// it's centered in relation to the property table
p := imgui.CursorPos()
if sz.Y < propertyTableHeight {
p.Y += (propertyTableHeight - sz.Y) / 2
imgui.SetCursorPos(p)
} else {
// if height of thumbnail is greater than or
// equal to height of property table then we add
// a imgui.Spacing(). this may expand the height
// of the property table but that's okay
imgui.Spacing()
}
imgui.Image(imgui.TextureID(win.boxartTexture.getID()), sz)
}
imgui.EndTable()
}
}()
}
imguiSeparator()
// imgui.Checkbox("Show all files", &win.showAllFiles)
// imgui.SameLine()
// imgui.Checkbox("Show hidden files", &win.showHidden)
// imgui.Spacing()
if imgui.Button("Cancel") {
// close rom selected in both the debugger and playmode
win.debuggerSetOpen(false)
@ -646,7 +631,7 @@ func (win *winSelectROM) setSelectedFile(filename string) {
}
// create cartridge loader and start thumbnail emulation
loader, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO")
cartload, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO")
if err != nil {
logger.Logf("ROM Select", err.Error())
return
@ -654,10 +639,10 @@ func (win *winSelectROM) setSelectedFile(filename string) {
// push function to emulation goroutine. result will be checked for in
// draw() function
win.img.dbg.PushPropertyLookup(loader.HashMD5, win.propertyResult)
win.img.dbg.PushPropertyLookup(cartload.HashMD5, win.propertyResult)
// create thumbnail animation
win.thmb.Create(loader, thumbnailer.UndefinedNumFrames, true)
win.thmb.Create(cartload, thumbnailer.UndefinedNumFrames, true)
// defer boxart lookup to when we receive the property
}

View file

@ -406,23 +406,6 @@ func (cart *Cartridge) Step(clock float32) {
cart.mapper.Step(clock)
}
// Hotload cartridge ROM into emulation. Not changing any other state of the
// emulation.
func (cart *Cartridge) HotLoad(cartload cartridgeloader.Loader) error {
if hl, ok := cart.mapper.(mapper.CartHotLoader); ok {
cart.Hash = cartload.HashSHA1
err := hl.HotLoad(cartload)
if err != nil {
return err
}
return nil
}
return fmt.Errorf("cartridge: %s does not support hotloading", cart.mapper.ID())
}
// GetRegistersBus returns interface to the registers of the cartridge or nil
// if cartridge has no registers.
func (cart *Cartridge) GetRegistersBus() mapper.CartRegistersBus {

View file

@ -16,7 +16,6 @@
package mapper
import (
"github.com/jetsetilly/gopher2600/cartridgeloader"
"github.com/jetsetilly/gopher2600/environment"
"github.com/jetsetilly/gopher2600/hardware/cpu"
"github.com/jetsetilly/gopher2600/hardware/memory/vcs"
@ -341,12 +340,6 @@ type CartRewindBoundary interface {
RewindBoundary() bool
}
// CartHotLoader is implemented by cartridge mappers that can be hot-loaded.
// ie. ROM data updated but keeping RAM memory intact.
type CartHotLoader interface {
HotLoad(cartridgeloader.Loader) error
}
// CartROMDump is implemented by cartridge mappers that can save themselves to disk.
type CartROMDump interface {
ROMDump(filename string) error