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.
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?
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.
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:
structContentView: View {
var body: some View {
List {
ForEach(0..<10, id: \.self) { _in MyView()
.myNewModifier()
}
}
}
}
structMyView: View {
var body: some View {
Text("MyView")
}
funcmyNewModifier() -> any View {
self.foregroundColor(.red)
}
}
The subtle error is that the signature of myNewModifier should have looked like this:
funcmyNewModifier() -> 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:
I write a function signature like
funcmyNewModifier() -> View
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]
I click Fix, and the scene is set for me to run around like a headless chook, looking for the mystery error.
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:
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:
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:
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 aroundlet 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:
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.
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):
Lights
So, things don’t look great with the default lighting. Let’s replace the defaults with our own light:
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:
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
classViewController: UIViewController {
@IBOutletweakvar sceneView: SCNView!
overridefuncviewDidLoad() {
super.viewDidLoad()
// create a brand-new scenelet 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
}
funccreateBoxNode() -> SCNNode {
// make a 1x1x1 boxlet box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1)
let boxNode = SCNNode(geometry: box)
return boxNode
}
funccreateOmniDirectionalLightNode() -> 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
}
funccreateCameraNode(lookingAt target: SCNNode) -> SCNNode {
let lookAt = SCNLookAtConstraint(target: target)
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.constraints = [lookAt]
return cameraNode
}
}
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:
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:
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:
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.