Cartoon drawing of a man with a grey beard and glasses.

coofdy.com

Martin Kenny's blog

Recent Posts…

Restoring old DOS backups in 2023

Recently, as part of an ongoing effort to preserve old work, and to free up physical storage space, I went through the process of imaging all my 3.5-inch floppy disks. I’ve been using my Kryoflux board to save off a .img file for each floppy.

While most of the disks contained .zip files, or multi-disk .zip archives (something for another post), there were several multi-disk backup sets created by some version of DOS Backup in the mid-nineties.

Each of these backup disks contained a single file with names like KK50306D.001. Looking at these files in a hex editor, we can see that they contain the string NORTON Ver 1E in their header. Apparently Microsoft licensed Norton Backup for inclusion in versions of DOS at around that time. The first bytes of a backup file, including the string NORTON Ver 1E

I fondly imagined that someone had written an open source tool for restoring these; I didn’t find one. I briefly thought about doing that myself.

Here are the steps I ended up taking to restore my backup sets:

  • It looked as though the only option was using the original DOS restore functionality. But which version of DOS?

  • Research led to DOS 6, 6.2, or 6.22 (see DoubleSpace vs DriveSpace and the Stac Electronics patent).

  • I installed DOS 6.22 in a VirtualBox VM and tried its version of msbackup. It couldn’t reconstruct the catalog from my disk images.

  • Some mucking around and it reconstructed the catalog from specially-named directories on disk, but when I tried to restore, it skipped all the files without explaining why.

  • So I installed Windows 3.11 and tried the Windows version of backup, mwbackup.

  • mwbackup told me that this backup was made by DOS 6 or 6.20, and pointed me to the DOS readme file, which explained that I could install the DOS 6 or 6.20 versions of msbackup over the 6.22 ones.

  • After accidentally trashing my VM, I rebuilt DOS 6.22 and installed the DOS 6 msbackup over the top.

  • This version was happy to reconstruct the catalog from floppy images, and I was able to restore all my backup sets.

  • The backups include the drive letter that the original files were backed up from. You can choose to restore to a different drive letter, however msbackup will prompt with an “Are you sure?” message for each new directory it needs to create. To avoid that, I used the DOS subst command to map my drive as the drive letter from the original backups, then picked the msbackup option to restore to the original drive letter.

Some links which helped me through this:

SwiftUI errors with any View vs some View

When writing SwiftUI code, Xcode sometimes complains with an error much like this:

Generic struct 'List' requires that 'some AccessibilityRotorContent' conform to 'View'
Initializer 'init(content:)' requires that 'some AccessibilityRotorContent' conform to 'View'
Struct 'ViewBuilder' requires that 'some AccessibilityRotorContent' conform to 'View'

When this happens, it turns out that I’ve just written something like this:

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0..<10, id: \.self) { _ in
                MyView()
                    .myNewModifier()
            }
        }
    }
}

struct MyView: View {
    var body: some View {
        Text("MyView")
    }
    
    func myNewModifier() -> any View {
        self.foregroundColor(.red)
    }
}

The subtle error is that the signature of myNewModifier should have looked like this:

func myNewModifier() -> some View

Note the some vs the any in my non-compiling code.

Postscript

I was going to put this down to inattention on my part, but as I was writing this post, I noticed what the sequence of events is:

  1. I write a function signature like

    func myNewModifier() -> View
    
  2. Xcode responds with the following error and Fix-it:

    Use of protocol 'View' as a type must be written 'any View'
    Replace 'View' with 'any View'  [Fix]
    
  3. I click Fix, and the scene is set for me to run around like a headless chook, looking for the mystery error.

SceneKit: first steps — Part 1: Just a box

Background

In the spirit of #showyourwork, and learning in the open, I’ve decided to document my journey as I teach myself about SceneKit on iOS and OS X, and modern 3D APIs.

Early 1980s

My first foray into 3D was sometime in the early 1980s, when I played with drawing a 3D model of the earth. My inspiration was most likely Robert Tinney’s cover for the May 1979 issue of BYTE magazine:

May 1979 BYTE cover

Unfortunately inspiration didn’t equal reality on my home-brewed TRS-80 clone. Its 128×48 graphics mode was a bit limiting, and state-of-the-art 3D on that platform looked like subLogic Flight Simulator:

A screenshot of subLogic Flight Simulator on the TRS-80

Mid 1990s

In the mid 1990s I was experimenting with projecting from 3D to 2D, then drawing directly to Windows’ GDI API. Later, I played around with Microsoft’s WinG API, then Direct3D.

Early 2000s

By about 2000, my inspiration was the movie Toy Story, and the game Train Simulator (fuelled by my then two-year-old son’s train obsession). I still have a bunch of books with titles like OpenGL Programming Guide (the “red book”), Advanced RenderMan, Real-Time Rendering, and Advanced Animation and Rendering Techniques.

At the time I played around with modelling the front of a locomotive, in code, and rendering it with OpenGL.

Now

Today’s inspiration are games like The Witness and Monument Valley:

A screenshot of The Witness A screenshot of Monument Valley

Getting started

Enough history. Let’s get started!

Through this series, I’m being guided by Apple’s SceneKit documentation, David Rönnqvist’s SceneKit article from objc.io, and his excellent iBooks book 3D Graphics with Scene Kit.

I’ll be doing all of these steps in a simple single-view application. The scene is presented in an SCNView that completly covers the app’s main view. The SCNView from the storyboard is accessed through an @IBOutlet called sceneView. The complete listing for the view controller can be found at the bottom of this post.

An empty scene

The first step is to create an empty scene. Note that I set the scence view’s background color to black, for that super-spooky look (and to make sure that the simple lighting looks realistic). I also get a reference to the scene’s root node, as a convenience for later:

let scene = SCNScene()
sceneView.scene = scene
sceneView.debugOptions = [] // [.ShowLightExtents, .ShowBoundingBoxes]
sceneView.backgroundColor = UIColor.blackColor()
sceneView.allowsCameraControl = true  // allow dragging the camera around
let rootNode = scene.rootNode

An actor

Next up we need something to look at. In this first test, it’s going to be a 1 × 1 × 1 cube with rounded edges. SceneKit provides the SCNBox class which creates some geometry in the shape of a rounded box. Add one of those to a node, and add that node to the root node of the scene, and we have our boxy actor:

func createBoxNode() -> SCNNode {
    // make a 1×1×1 box
    let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1)
    let boxNode = SCNNode(geometry: box)
    return boxNode
}



let boxNode = createBoxNode()
rootNode.addChildNode(boxNode)

Camera

At this point the scene will render, but with a default light and camera. The position of the default camera means that the cube will fill the entire viewport. To fix that we need our own camera.

func createCameraNode(lookingAt target: SCNNode) -> SCNNode {
    let lookAt = SCNLookAtConstraint(target: target)
    lookAt.gimbalLockEnabled = true

    let camera = SCNCamera()
    camera.xFov = 60.0;

    let cameraNode = SCNNode()
    cameraNode.camera = camera
    cameraNode.constraints = [lookAt]
    return cameraNode
}



let cameraNode = createCameraNode(lookingAt: boxNode)
cameraNode.position = SCNVector3(x: 2, y: 1.5, z: 2.5)
rootNode.addChildNode(cameraNode)

Here I’ve setup a camera with a field-of-view of 60º in the x-axis (xFov), a constraint that keeps the camera looking at the box, and with gimbal-lock enabled to keep the horizon level if the camera moves.

With the camera moved into place, here’s what we can see (remember that we’re still using the default light source):

A cube rendered with default lighting

Lights

So, things don’t look great with the default lighting. Let’s replace the defaults with our own light:

func createOmniDirectionalLightNode() -> SCNNode {
    let omniLight = SCNLight()
    omniLight.type = SCNLightTypeOmni
    omniLight.color = UIColor.init(red: 1, green: 0.75, blue: 0.75, alpha: 1)
    omniLight.attenuationStartDistance = 1;
    omniLight.attenuationEndDistance = 10;

    let omniLightNode = SCNNode()
    omniLightNode.light = omniLight
    return omniLightNode
}



let omniLightNode = createOmniDirectionalLightNode()
omniLightNode.position = SCNVector3(x: 1, y: 1.5, z: 1)
rootNode.addChildNode(omniLightNode)

I’ve placed a light pink (rose gold!) omnidirectional light to the right, above, and in-front of the box. I’ve also used attenuationStartDistance and attenuationEndDistance to make sure that the brightness falls off with distance from the light. Here’s what it looks like now:

A cube rendered with one omnidirectional light

The cube is starting to look better, but some better materials and texture could definitely spruce things up. Next time I’ll look at adding some of those.

ViewController.swift

class ViewController: UIViewController {
    @IBOutlet weak var sceneView: SCNView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // create a brand-new scene
        let scene = SCNScene()
        let rootNode = scene.rootNode

        let boxNode = createBoxNode()
        rootNode.addChildNode(boxNode)

        let omniLightNode = createOmniDirectionalLightNode()
        omniLightNode.position = SCNVector3(x: 1, y: 1.5, z: 1)
        rootNode.addChildNode(omniLightNode)

        let cameraNode = createCameraNode(lookingAt: boxNode)
        cameraNode.position = SCNVector3(x: 2, y: 1.5, z: 2.5)
        rootNode.addChildNode(cameraNode)

        sceneView.pointOfView = cameraNode
        sceneView.scene = scene
    }

    func createBoxNode() -> SCNNode {
        // make a 1x1x1 box
        let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1)

        let boxNode = SCNNode(geometry: box)
        return boxNode
    }

    func createOmniDirectionalLightNode() -> SCNNode {
        let omniLight = SCNLight()
        omniLight.type = SCNLightTypeOmni
        omniLight.color = UIColor.init(red: 1, green: 0.75, blue: 0.75, alpha: 1)
        omniLight.attenuationStartDistance = 1;
        omniLight.attenuationEndDistance = 10;

        let omniLightNode = SCNNode()
        omniLightNode.light = omniLight
        return omniLightNode
    }

    func createCameraNode(lookingAt target: SCNNode) -> SCNNode {
        let lookAt = SCNLookAtConstraint(target: target)
        let camera = SCNCamera()

        let cameraNode = SCNNode()
        cameraNode.camera = camera
        cameraNode.constraints = [lookAt]
        return cameraNode
    }
}

A fix for cwRsync ignoring --delete

I was just helping Michael set up a Windows rsync-based solution to sync/backup his symbolic-linked game saves to a central folder (for subsequent syncing with BitTorrent Sync). We installed cwRsync, then used a command-line like this:

rsync -aL /cygdrive/C/Users/michael/GameSaves/ /cygdrive/C/Users/michael/Backups/

Copying worked without a hitch, but then we added --delete to the command-line. This should delete any files from the Backups folder that have been deleted from the GameSaves folder since the last sync:

rsync -aL --delete /cygdrive/C/Users/michael/GameSaves/ /cygdrive/C/Users/michael/Backups/

Unlike the copying, this proved difficult to get working. After longer than I care to admit, I listened to my Spidey-sense, and switched the upper-case drive letters to lower-case. Suddenly the deletes started working! The final command-line looked like this:

rsync -aL --delete /cygdrive/c/Users/michael/GameSaves/ /cygdrive/c/Users/michael/Backups/

So, it seems that cwRsync (and possibly a full Cygwin rsync installation) doesn’t like upper-cased drive letters in its paths.

Fixing 'could not find symbol value for runtime.buildVersion' Delve debugger error

TL;DR

If you’re trying to debug Go code using the Delve debugger, or the Go plug-in for IntelliJ (which uses Delve), and you get the following error:

could not launch process: Could not determine version number:
  could not find symbol value for runtime.buildVersion

you should upgrade your go environment to a newer version.

Details

Yesterday, I decided I needed a better way to debug Go code than using fmt.Println. I discovered that there’s a work-in-progress Go plug-in for IntelliJ. After installing the latest IntelliJ IDEA Community Edition, and installing the plug-in, I was dissapointed to be greeted with the could not find symbol value for runtime.buildVersion error when I tried to debug.

After searching through the Delve code, I decided that the most likely fix was to upgrade my go installation. At that point I was running version 1.3.3. After upgrading to 1.5.1, I can now happily go about my debugging.