From 28ae543e3654ef801019e2d26091a92af86288b6 Mon Sep 17 00:00:00 2001 From: JetSetIlly Date: Tue, 16 Apr 2024 19:42:20 +0100 Subject: [PATCH] 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 --- cartridgeloader/doc.go | 99 ++----- cartridgeloader/loader.go | 12 +- comparison/comparison.go | 1 + debugger/commands.go | 27 +- debugger/commands_template.go | 2 +- debugger/debugger.go | 105 +++---- gui/sdlimgui/win_rom_select.go | 319 ++++++++++----------- hardware/memory/cartridge/cartridge.go | 17 -- hardware/memory/cartridge/mapper/mapper.go | 7 - 9 files changed, 217 insertions(+), 372 deletions(-) diff --git a/cartridgeloader/doc.go b/cartridgeloader/doc.go index d38fcdd1..6f1800bc 100644 --- a/cartridgeloader/doc.go +++ b/cartridgeloader/doc.go @@ -13,37 +13,16 @@ // You should have received a copy of the GNU General Public License // along with Gopher2600. If not, see . -// 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 diff --git a/cartridgeloader/loader.go b/cartridgeloader/loader.go index 299d5551..a06fdace 100644 --- a/cartridgeloader/loader.go +++ b/cartridgeloader/loader.go @@ -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) } diff --git a/comparison/comparison.go b/comparison/comparison.go index 5cebac31..3c53db4e 100644 --- a/comparison/comparison.go +++ b/comparison/comparison.go @@ -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? diff --git a/debugger/commands.go b/debugger/commands.go index 6836c281..08d801b8 100644 --- a/debugger/commands.go +++ b/debugger/commands.go @@ -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": diff --git a/debugger/commands_template.go b/debugger/commands_template.go index 61f6014e..f390565a 100644 --- a/debugger/commands_template.go +++ b/debugger/commands_template.go @@ -96,7 +96,7 @@ var commandTemplate = []string{ cmdGoto + " [%N] (%N) (%N)", cmdInsert + " %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 + " %S", cmdDisasm + " (BYTECODE|REDUX)", cmdGrep + " (OPERATOR|OPERAND|COPROC) %S", diff --git a/debugger/debugger.go b/debugger/debugger.go index e4e28969..08aad27b 100644 --- a/debugger/debugger.go +++ b/debugger/debugger.go @@ -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) diff --git a/gui/sdlimgui/win_rom_select.go b/gui/sdlimgui/win_rom_select.go index b8c1e117..b146262c 100644 --- a/gui/sdlimgui/win_rom_select.go +++ b/gui/sdlimgui/win_rom_select.go @@ -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 } diff --git a/hardware/memory/cartridge/cartridge.go b/hardware/memory/cartridge/cartridge.go index 3b8292b1..3e0ad3c5 100644 --- a/hardware/memory/cartridge/cartridge.go +++ b/hardware/memory/cartridge/cartridge.go @@ -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 { diff --git a/hardware/memory/cartridge/mapper/mapper.go b/hardware/memory/cartridge/mapper/mapper.go index 8134a14f..d51c3c1d 100644 --- a/hardware/memory/cartridge/mapper/mapper.go +++ b/hardware/memory/cartridge/mapper/mapper.go @@ -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