Library for generating map images for StarCraft: Brood War and Remastered.
Both map files (.scm and .scx) as well as replay files (.rep) are supported as input.
This is a wrapper library around the bw-chk, jssuh and scm-extractor libraries made by the Shield Battery project. All the heavy lifting is done by their code—this library mostly just simplifies the process and passes the result through an image encoder.
This library is available via npm.
npm i --save @dada78641/bwmapimage
Basic usage involves passing a filename and getting an image buffer in return:
import fs from 'fs/promises'
import {BwMapImage} from '@dada78641/bwmapimage'
const bwMapImage = new BwMapImage('(4)Vermeer SE_2.1.scm', {})
const [buffer, metadata] = await bwMapImage.renderMapImage()
await fs.writeFile(`out${metadata.extension}`, buffer, null)
By default, the library will generate a full size .png file (which can take a bit of time and be quite large).
Function:
new BwMapImage(file, options)
Parameters:
file
string|buffer
Either a path to a .scm, .scx or .rep file; or a buffer of one of such files.options
object|null
See below.
Returns:
A BwMapImage
object for the given file and options object.
For the source file, you can pass either a string or a buffer. If a string is passed, the file will be read from disk, with the file type assumed from the extension; a buffer will be processed in the same way.
Function:
await bwMapImage.renderMapImage()
Returns:
buffer
string
Image buffer that can be directly saved to a file.metadata.type
string
Either"map"
or"replay"
.metadata.extension
string
A recommended file extension for the given format, e.g.".jpg"
metadata.format
string
Image format used.metadata.channels
number
Number of channels in the output image.metadata.size
number
Size of the buffer in bytes.metadata.width
number
Final width of the image buffer after resizing.metadata.height
number
Final height of the image buffer after resizing.metadata.fullWidth
number
Full width of the bitmap prior to resizing.metadata.fullHeight
number
Full height of the bitmap prior to resizing.metadata.mapTitle
string
Raw title for the map. Can contain escape sequences.metadata.mapTitleStripped
string
Title for the map with escape sequences stripped.metadata.mapDescription
string
Description for the map.metadata.mapHash
string
XXH64 hash of the map data, for caching purposes.metadata.mapTileWidth
number
Map width in tiles.metadata.mapTileHeight
number
Map height in tiles.metadata.mapTilesetName
string
Name of the tileset used.metadata.mapTilesetId
number
Tileset ID for the map—see sctoolsdata for more information.metadata.mapEncoding
string
Character encoding for the map; typically either"cp949"
,"cp1252"
or"utf8"
.metadata.resolvedOptions
object
The user's passed options with default options merged in.
Generates an image from a map or replay file.
Function:
await bwMapImage.getMapMetadata()
Returns:
metadata.mapTitle
string
Raw title for the map. Can contain escape sequences.metadata.mapTitleStripped
string
Title for the map with escape sequences stripped.metadata.mapDescription
string
Description for the map.metadata.mapHash
string
XXH64 hash of the map data, for caching purposes.metadata.mapTileWidth
number
Map width in tiles.metadata.mapTileHeight
number
Map height in tiles.metadata.mapTilesetName
string
Name of the tileset used.metadata.mapTilesetId
number
Tileset ID for the map—see @dada78641/bwtoolsdata for more information.metadata.mapEncoding
string
Character encoding for the map; typically either"cp949"
,"cp1252"
or"utf8"
.
This returns metadata about the map data itself, before the image is generated. This allows for you to, for example, check the mapHash
against a cache.
Note that the hash is specific to the map data, not to the generated image. It's designed specifically to be used for caching purposes, in that two maps with the same hash will always produce the same image.
The following options can be passed:
Setting | Type | Default | Notes |
---|---|---|---|
bwGraphicsPath | string|null | null |
If null, this gets set to ~/.config/bwmapimage, where ~ is the user's home directory. |
forceType | string|null | null |
Either "map" or "replay" , but normally autodetected. |
tileSize | number | 32 |
Either 32 , 16 or 8 . Base tile size; 32 is full size, anything smaller results in smaller images for much less processing time and memory. |
encoderType | string | "png" |
E.g. "png" , "jpeg" , "avif" , etc—anything supported by sharp. |
encoderOptions | object | {} |
Options passed on to sharp for the given output type. |
targetWidth | number|null | null |
Width the image will be resized to. See target size note below. |
targetHeight | number|null | null |
Height the image will be resized to. |
targetFit | string|null | "inside" |
Resizing fit; typically you want "inside" . See the sharp api for other options. |
preEncodeHook | function|null | null |
Callback for manually utilizing the sharp object with the full size map image buffer (prior to resizing). |
Note that, by default, this library will create a lossless .png file at full size. This is extremely time consuming (on a fast machine it can take 3-5 seconds) and results in huge files (10–15 MB, 4096×4096 for a 128×128 tile map). It will also take a lot of memory to generate the initial uncompressed bitmap.
To downscale the resulting image, you only need to set one of targetWidth
or targetHeight
; if you set one, the final image will be resized within a square by that size. So if you set one of them to 512
, a square map like Fighting Spirit will end up being 512×512, and a slightly thinner map like Invader will be 512×448.
The preEncodeHook
value can be used to manually do something with the sharp object before the image is finalized and returned. For example, here's a function that boosts the brightness of the generated images:
/** Converts Photoshop style levels to linear multiplier/offset values. */
function levelsToLinear(min, max) {
const a = 255 / (max - min)
const b = -min * a
return [a, b]
}
await bwMapImage.renderMapImage('map.scm', {
// see: https://sharp.pixelplumbing.com/api-operation#linear
preEncodeHook: image => image.linear(...levelsToLinear(0, 150))
})
The callback runs prior to resizing and compression.
To be able to generate map images, you'll need to extract the required graphics from StarCraft.
A copy of the required graphics can be found here (18.5M), provided for convenience reasons.
You can also extract them from the game files directly using a program such as CascView.
The following files are needed:
- all files listed in units.js and sprites.js files from bw-chk;
- for all tilesets
['badlands', 'platform', 'install', 'ashworld', 'jungle', 'desert', 'ice', 'twilight']
, all files of extension['.cv5', '.vx4', '.vr4', '.wpe', '.vx4ex']
.
All filenames should be lowercase, as that's how bw-chk expects them to be (although on case insensitive filesystems this doesn't matter).
By default, the library will look for the graphics in the ~/.config/bwmapimage directory.
- bw-chk, jssuh and scm-extractor – libraries that do most of the work
- sharp – library used for final image generation
- StarCraft map graphics package
MIT license.