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