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 // You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>. // along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
// Package cartridgeloader is used to specify the cartridge data that is to be // Package cartridgeloader is used to load cartridge data so that it can be used
// attached to the emulated VCS. // with the cartridge pacakage
//
// 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.
// //
// # File Extensions // # File Extensions
// //
// The file extension of a file will specify the cartridge mapping and will // 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 // cause the emulation to use that mapping.
// extension "bin" but sometimes it is necessary to specify explicitly what
// the mapper is.
// //
// 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 2k "2k"
// Atari 4k "4k" // Atari 4k "4k"
@ -59,7 +38,7 @@
// Parker Bros "E0" // Parker Bros "E0"
// M-Network "E7" // M-Network "E7"
// Tigervision "3F" // Tigervision "3F"
// Supercharger "AR" // Supercharger "AR", "MP3, "WAV"
// DF "DF" // DF "DF"
// 3E "3E" // 3E "3E"
// 3E+ "3E+" // 3E+ "3E+"
@ -67,64 +46,18 @@
// DPC (Pitfall2) "DPC" // DPC (Pitfall2) "DPC"
// DPC+ "DP+" // DPC+ "DP+"
// CDF "CDF" (including CDFJ) // CDF "CDF" (including CDFJ)
// MovieCart "MVC"
// //
// In addition to these above there a three "special" mapping formats. Which // File extensions are case insensitive.
// aren't really mappers in the normal sense but which will none-the-less
// trigger a particular cartridge emulation path.
// //
// "MP3" and "WAV" indicate that the emulation should use the Supercharger // A file extension of "BIN", "ROM", "A26" indicates that the data should be
// mapper but with the understanding that the data will be loaded from audio // fingerprinted as normal.
// data.
// //
// "MVC" indicates that the data is a MovieCart stream. Because of the // # Hashes
// potential size of these files, data is streamed from the file. Also,
// steaming over HTTP is not yet currently supported.
// //
// Finally a file extension of "BIN", "ROM", "A26" will tell the mapping system // Creating a cartridge loader with NewLoaderFromFilename() or
// to "fingerprint" the cartridge data to ascertain the mapping type. // 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
// Fingerprinting is not handled by the cartridlgeloader package. // this will mean the hash is taken using all the data but some cartridge are
// // likely to have much more data than that.
// # 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.
package cartridgeloader package cartridgeloader

View file

@ -34,6 +34,8 @@ import (
// the maximum amount of data to load into the peep slice // the maximum amount of data to load into the peep slice
const maxPeepLength = 1048576 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 { func peepData(data []byte) []byte {
return data[:min(len(data), maxPeepLength)] return data[:min(len(data), maxPeepLength)]
} }
@ -74,8 +76,8 @@ type Loader struct {
// help fingerprinting and for creating the SHA1 and MD5 hashes // help fingerprinting and for creating the SHA1 and MD5 hashes
// //
// in reality, most cartridges are small enough to fit entirely inside the // in reality, most cartridges are small enough to fit entirely inside the
// peep slice. currently it is only moviecart data and maybe supercharger // peep field. currently it is only moviecart data and supercharger sound
// sound files that will be larger than the maxPeepLength // files that are ever arger than maxPeepLength
peep []byte peep []byte
// data was supplied through NewLoaderFromData() // data was supplied through NewLoaderFromData()
@ -256,19 +258,19 @@ func (ld Loader) Size() int {
return ld.size 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 { func (ld Loader) Contains(subslice []byte) bool {
return bytes.Contains(ld.peep, subslice) 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 // zero and limit
func (ld Loader) ContainsLimit(limit int, subslice []byte) bool { func (ld Loader) ContainsLimit(limit int, subslice []byte) bool {
limit = min(limit, ld.Size()) limit = min(limit, ld.Size())
return bytes.Contains(ld.peep[:limit], subslice) 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 { func (ld Loader) Count(subslice []byte) int {
return bytes.Count(ld.peep, subslice) return bytes.Count(ld.peep, subslice)
} }

View file

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

View file

@ -25,7 +25,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/jetsetilly/gopher2600/cartridgeloader"
"github.com/jetsetilly/gopher2600/coprocessor" "github.com/jetsetilly/gopher2600/coprocessor"
coproc_breakpoints "github.com/jetsetilly/gopher2600/coprocessor/developer/breakpoints" coproc_breakpoints "github.com/jetsetilly/gopher2600/coprocessor/developer/breakpoints"
"github.com/jetsetilly/gopher2600/coprocessor/developer/callstack" "github.com/jetsetilly/gopher2600/coprocessor/developer/callstack"
@ -472,21 +471,11 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
case cmdInsert: case cmdInsert:
dbg.unwindLoop(func() error { dbg.unwindLoop(func() error {
cart, _ := tokens.Get() filename, _ := tokens.Get()
cl, err := cartridgeloader.NewLoaderFromFilename(cart, "AUTO") err := dbg.insertCartridge(filename)
if err != nil { if err != nil {
return err 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 return nil
}) })
@ -573,14 +562,6 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
dbg.printLine(terminal.StyleFeedback, "cartridge has no RAM") 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": case "DUMP":
romdump, err := dbg.vcs.Mem.Cart.ROMDump() romdump, err := dbg.vcs.Mem.Cart.ROMDump()
if err != nil { if err != nil {
@ -1496,7 +1477,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
case "DUMP": case "DUMP":
dump := func(name string) { dump := func(name string) {
if data, ok := static.Reference(name); ok { 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) fn = fmt.Sprintf("%s.bin", fn)
err := os.WriteFile(fn, data, 0644) err := os.WriteFile(fn, data, 0644)
if err != nil { if err != nil {
@ -1799,7 +1780,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
if ok { if ok {
switch strings.ToUpper(controller) { switch strings.ToUpper(controller) {
case "AUTO": case "AUTO":
dbg.vcs.FingerprintPeripheral(id, *dbg.loader) dbg.vcs.FingerprintPeripheral(id, *dbg.cartload)
case "STICK": case "STICK":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewStick) err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewStick)
case "PADDLE": case "PADDLE":

View file

@ -96,7 +96,7 @@ var commandTemplate = []string{
cmdGoto + " [%<clock>N] (%<scanline>N) (%<frame>N)", cmdGoto + " [%<clock>N] (%<scanline>N) (%<frame>N)",
cmdInsert + " %<cartridge>F", 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", cmdPatch + " %<patch file>S",
cmdDisasm + " (BYTECODE|REDUX)", cmdDisasm + " (BYTECODE|REDUX)",
cmdGrep + " (OPERATOR|OPERAND|COPROC) %<search>S", cmdGrep + " (OPERATOR|OPERAND|COPROC) %<search>S",

View file

@ -99,9 +99,8 @@ type Debugger struct {
// components may change (during rewind for example) // components may change (during rewind for example)
vcs *hardware.VCS vcs *hardware.VCS
// the last loader to be used. we keep a reference to it so we can make // keep a reference to the current cartridgeloader to make sure Close() is called
// sure Close() is called on end cartload *cartridgeloader.Loader
loader *cartridgeloader.Loader
// preview emulation is used to gather information about a ROM before // preview emulation is used to gather information about a ROM before
// running it fully // 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) err = dbg.attachCartridge(cartload)
if err != nil { if err != nil {
return fmt.Errorf("debugger: %w", err) return fmt.Errorf("debugger: %w", err)
@ -778,24 +779,25 @@ func (dbg *Debugger) StartInPlayMode(filename string) error {
// set running flag as early as possible // set running flag as early as possible
dbg.running = true dbg.running = true
var err error err := recorder.IsPlaybackFile(filename)
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)
if err != nil { if err != nil {
if !errors.Is(err, recorder.NotAPlaybackFile) { if !errors.Is(err, recorder.NotAPlaybackFile) {
return fmt.Errorf("debugger: %w", err) 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) err = dbg.attachCartridge(cartload)
if err != nil { if err != nil {
return fmt.Errorf("debugger: %w", err) return fmt.Errorf("debugger: %w", err)
@ -940,8 +942,8 @@ func (dbg *Debugger) run() error {
// make sure any cartridge loader has been finished with // make sure any cartridge loader has been finished with
defer func() { defer func() {
if dbg.loader != nil { if dbg.cartload != nil {
err := dbg.loader.Close() err := dbg.cartload.Close()
if err != nil { if err != nil {
logger.Logf("debugger", err.Error()) 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 // if the new cartridge loader has the same filename as the previous loader
// then reset() is called with a newCartridge argument of false. // 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) { func (dbg *Debugger) attachCartridge(cartload cartridgeloader.Loader) (e error) {
// is this a new cartridge we're loading. value is used for dbg.reset() // 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 // stop optional sub-systems that shouldn't survive a new cartridge insertion
dbg.endPlayback() dbg.endPlayback()
@ -1169,13 +1174,13 @@ func (dbg *Debugger) attachCartridge(cartload cartridgeloader.Loader) (e error)
}() }()
// close any existing loader before continuing // close any existing loader before continuing
if dbg.loader != nil { if dbg.cartload != nil {
err := dbg.loader.Close() err := dbg.cartload.Close()
if err != nil { if err != nil {
return err logger.Logf("debuger", err.Error())
} }
} }
dbg.loader = &cartload dbg.cartload = &cartload
// reset of vcs is implied with attach cartridge // reset of vcs is implied with attach cartridge
err := setup.AttachCartridge(dbg.vcs, cartload, false) err := setup.AttachCartridge(dbg.vcs, cartload, false)
@ -1332,11 +1337,14 @@ func (dbg *Debugger) startComparison(comparisonROM string, comparisonPrefs strin
return err return err
} }
// cartload is passed to comparision.CreateFromLoader(). closure will be
// handled from there
cartload, err := cartridgeloader.NewLoaderFromFilename(comparisonROM, "AUTO") cartload, err := cartridgeloader.NewLoaderFromFilename(comparisonROM, "AUTO")
if err != nil { if err != nil {
return err return err
} }
// comparision emulation handles closure of cartridgeloader
dbg.comparison.CreateFromLoader(cartload) dbg.comparison.CreateFromLoader(cartload)
// check use of comparison prefs // 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 // parseInput splits the input into individual commands. each command is then
// passed to parseCommand for processing // passed to parseCommand for processing
// //
@ -1453,7 +1418,7 @@ func (dbg *Debugger) Plugged(port plugging.PortID, peripheral plugging.Periphera
} }
func (dbg *Debugger) reloadCartridge() error { func (dbg *Debugger) reloadCartridge() error {
if dbg.loader == nil { if dbg.cartload == nil {
return nil return nil
} }
@ -1462,7 +1427,7 @@ func (dbg *Debugger) reloadCartridge() error {
dbg.macro.Reset() 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. // 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 { func (dbg *Debugger) insertCartridge(filename string) error {
if filename == "" { if filename == "" {
filename = dbg.loader.Filename filename = dbg.cartload.Filename
} }
cartload, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO") cartload, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO")
@ -1480,6 +1445,8 @@ func (dbg *Debugger) insertCartridge(filename string) error {
return fmt.Errorf("debugger: %w", err) 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) err = dbg.attachCartridge(cartload)
if err != nil { if err != nil {
return fmt.Errorf("debugger: %w", err) return fmt.Errorf("debugger: %w", err)

View file

@ -396,187 +396,172 @@ func (win *winSelectROM) draw() {
// control buttons. start controlHeight measurement // control buttons. start controlHeight measurement
win.controlHeight = imguiMeasureHeight(func() { win.controlHeight = imguiMeasureHeight(func() {
func() { imgui.SetNextItemOpen(win.informationOpen, imgui.ConditionAlways)
if !win.thmb.IsEmulating() { if !imgui.CollapsingHeaderV(win.selectedName, imgui.TreeNodeFlagsNone) {
imgui.PushItemFlag(imgui.ItemFlagsDisabled, true) win.informationOpen = false
imgui.PushStyleVarFloat(imgui.StyleVarAlpha, disabledAlpha) } else {
defer imgui.PopItemFlag() win.informationOpen = true
defer imgui.PopStyleVar() 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) // property table. we measure the height of this table to
if !imgui.CollapsingHeaderV(win.selectedName, imgui.TreeNodeFlagsNone) { // help centering the box art image in the next column
win.informationOpen = false imgui.TableNextRow()
} else { imgui.TableNextColumn()
win.informationOpen = true propertyTableTop := imgui.CursorPosY()
if imgui.BeginTable("#properties", 3) { if imgui.BeginTable("#properties", 2) {
imgui.TableSetupColumnV("#information", imgui.TableColumnFlagsWidthStretch, -1, 0) imgui.TableSetupColumnV("#category", imgui.TableColumnFlagsWidthFixed, -1, 0)
imgui.TableSetupColumnV("#spacingA", imgui.TableColumnFlagsWidthFixed, -1, 1) imgui.TableSetupColumnV("#detail", imgui.TableColumnFlagsWidthFixed, -1, 1)
imgui.TableSetupColumnV("#boxart", imgui.TableColumnFlagsWidthFixed, -1, 2)
// 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.TableNextRow()
imgui.TableNextColumn() imgui.TableNextColumn()
propertyTableTop := imgui.CursorPosY() imgui.Text("Name")
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.TableNextColumn() imgui.TableNextColumn()
if win.selectedProperties.IsValid() {
imgui.Text(win.selectedProperties.Name)
} else {
imgui.Text(win.selectedName)
}
if win.boxartUse { // results of preview emulation from the thumbnailer
imgui.TableNextColumn() selectedFilePreview := win.thmb.PreviewResults()
sz := imgui.Vec2{float32(win.boxartDimensions.X), float32(win.boxartDimensions.Y)}
// if thumbnail height is less than height of imgui.TableNextRow()
// property table then we position the image so that imgui.TableNextColumn()
// it's centered in relation to the property table imgui.AlignTextToFramePadding()
p := imgui.CursorPos() imgui.Text("Mapper")
if sz.Y < propertyTableHeight { imgui.TableNextColumn()
p.Y += (propertyTableHeight - sz.Y) / 2 if selectedFilePreview != nil {
imgui.SetCursorPos(p) imgui.Text(selectedFilePreview.VCS.Mem.Cart.ID())
} else { }
// if height of thumbnail is greater than or
// equal to height of property table then we add imgui.TableNextRow()
// a imgui.Spacing(). this may expand the height imgui.TableNextColumn()
// of the property table but that's okay imgui.PushItemFlag(imgui.ItemFlagsDisabled, true)
imgui.Spacing() 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() 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() imguiSeparator()
// imgui.Checkbox("Show all files", &win.showAllFiles)
// imgui.SameLine()
// imgui.Checkbox("Show hidden files", &win.showHidden)
// imgui.Spacing()
if imgui.Button("Cancel") { if imgui.Button("Cancel") {
// close rom selected in both the debugger and playmode // close rom selected in both the debugger and playmode
win.debuggerSetOpen(false) win.debuggerSetOpen(false)
@ -646,7 +631,7 @@ func (win *winSelectROM) setSelectedFile(filename string) {
} }
// create cartridge loader and start thumbnail emulation // create cartridge loader and start thumbnail emulation
loader, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO") cartload, err := cartridgeloader.NewLoaderFromFilename(filename, "AUTO")
if err != nil { if err != nil {
logger.Logf("ROM Select", err.Error()) logger.Logf("ROM Select", err.Error())
return return
@ -654,10 +639,10 @@ func (win *winSelectROM) setSelectedFile(filename string) {
// push function to emulation goroutine. result will be checked for in // push function to emulation goroutine. result will be checked for in
// draw() function // draw() function
win.img.dbg.PushPropertyLookup(loader.HashMD5, win.propertyResult) win.img.dbg.PushPropertyLookup(cartload.HashMD5, win.propertyResult)
// create thumbnail animation // 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 // 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) 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 // GetRegistersBus returns interface to the registers of the cartridge or nil
// if cartridge has no registers. // if cartridge has no registers.
func (cart *Cartridge) GetRegistersBus() mapper.CartRegistersBus { func (cart *Cartridge) GetRegistersBus() mapper.CartRegistersBus {

View file

@ -16,7 +16,6 @@
package mapper package mapper
import ( import (
"github.com/jetsetilly/gopher2600/cartridgeloader"
"github.com/jetsetilly/gopher2600/environment" "github.com/jetsetilly/gopher2600/environment"
"github.com/jetsetilly/gopher2600/hardware/cpu" "github.com/jetsetilly/gopher2600/hardware/cpu"
"github.com/jetsetilly/gopher2600/hardware/memory/vcs" "github.com/jetsetilly/gopher2600/hardware/memory/vcs"
@ -341,12 +340,6 @@ type CartRewindBoundary interface {
RewindBoundary() bool 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. // CartROMDump is implemented by cartridge mappers that can save themselves to disk.
type CartROMDump interface { type CartROMDump interface {
ROMDump(filename string) error ROMDump(filename string) error