mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-22 14:32:19 -04:00
removed all messages from errors package. the strings are now hard coded in place. the original reason for extracting the error strings like that was (a) for redundancy and (b) for localisation possibilities. However, in reality there is not much redundancy and since the project started the scope of localisation is much larger (particularly due to the addition of a GUI) this is the first step. the next step is to remove the errors package altogether. recent additions to Go mean that the functionality of the errors package is no longer required. moreover, the original idea for the errors package functions turns out not to have been as useful as first appeared (the Error() functionality which makes sure no repeating sub-strings occur is probably better achieved with static tooling). one idea that comes from this which is quite interesting is the idea of a curated error. that is, any error that has been "wrapped" as some 'generic' type. the IsAny() function in the errors package effectively serves this purpose. we use that in one place in the input loop of the debugger. not strictly necessary but nice to have so it would ideal if we could remove the errors package but keep that idea.
132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package patch
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/jetsetilly/gopher2600/errors"
|
|
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge"
|
|
"github.com/jetsetilly/gopher2600/paths"
|
|
)
|
|
|
|
const patchPath = "patches"
|
|
|
|
const commentLeader = '-'
|
|
const pokeLineSeparator = ":"
|
|
|
|
// CartridgeMemory applies the contents of a patch file to cartridge memory.
|
|
// Currently, patch file must be in the patches sub-directory of the
|
|
// resource path (see paths package).
|
|
func CartridgeMemory(mem *cartridge.Cartridge, patchFile string) (bool, error) {
|
|
var err error
|
|
|
|
p, err := paths.ResourcePath(patchPath, patchFile)
|
|
if err != nil {
|
|
return false, errors.Errorf("patch: %v", err)
|
|
}
|
|
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case *os.PathError:
|
|
return false, errors.Errorf("patch: %v", fmt.Sprintf("patch file not found (%s)", p))
|
|
}
|
|
return false, errors.Errorf("patch: %v", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
// make sure we're at the beginning of the file
|
|
if _, err = f.Seek(0, io.SeekStart); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
buffer, err := ioutil.ReadAll(f)
|
|
|
|
// once a patch has been made then we'll flip patched to true and return it
|
|
// to the calling function
|
|
patched := false
|
|
|
|
// walk through lines
|
|
lines := strings.Split(string(buffer), "\n")
|
|
for i := 0; i < len(lines); i++ {
|
|
|
|
// ignore empty lines
|
|
if len(lines[i]) == 0 {
|
|
continue // for loop
|
|
}
|
|
|
|
// ignoring comment lines and lines starting with whitespace
|
|
if lines[i][0] == commentLeader || unicode.IsSpace(rune(lines[i][0])) {
|
|
continue // for loop
|
|
}
|
|
|
|
pokeLine := strings.Split(lines[i], pokeLineSeparator)
|
|
|
|
// ignore any lines that don't match the required [offset: values...] format
|
|
if len(pokeLine) != 2 {
|
|
continue // for loop
|
|
}
|
|
|
|
// trim space around each poke line part
|
|
pokeLine[0] = strings.TrimSpace(pokeLine[0])
|
|
pokeLine[1] = strings.TrimSpace(pokeLine[1])
|
|
|
|
// parse offset
|
|
offset, err := strconv.ParseInt(pokeLine[0], 16, 16)
|
|
if err != nil {
|
|
continue // for loop
|
|
}
|
|
|
|
// split values into parts
|
|
values := strings.Split(pokeLine[1], " ")
|
|
for j := 0; j < len(values); j++ {
|
|
|
|
// trim space around each value
|
|
values[j] = strings.TrimSpace(values[j])
|
|
|
|
// ignore empty fields
|
|
if values[j] == "" {
|
|
continue // inner for loop
|
|
}
|
|
|
|
// covert data
|
|
v, err := strconv.ParseUint(values[j], 16, 8)
|
|
if err != nil {
|
|
continue // inner for loop
|
|
}
|
|
|
|
// patch memory
|
|
err = mem.Patch(int(offset), uint8(v))
|
|
if err != nil {
|
|
return patched, errors.Errorf("patch: %v", err)
|
|
}
|
|
patched = true
|
|
|
|
// advance offset
|
|
offset++
|
|
}
|
|
}
|
|
|
|
return patched, nil
|
|
}
|