├── .gitignore ├── LICENSE ├── README.md ├── assets └── DSStore-clean ├── index.js ├── lib ├── buddy-allocator.js ├── ds-store.js ├── entry.js └── partition.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Linus Unnebäck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-ds-store 2 | 3 | .DS_Store manipulation and creation from node.js 4 | 5 | ## Status 6 | 7 | Currently the implementation uses a pre-created `.DS_Store` file 8 | which it then modifies to suit the needs. This places several 9 | limitations and also only allows creating new files from scratch. 10 | 11 | ## Installation 12 | 13 | ```sh 14 | npm install ds-store 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```javscript 20 | var DSStore = require('ds-store'); 21 | 22 | var file = new DSStore(); 23 | ``` 24 | 25 | ## API 26 | 27 | ### file.setBackgroundPath(path) 28 | 29 | Set the background image to file specified by `path`. 30 | 31 | ### file.setBackgroundColor(red, green, blue) 32 | 33 | Set the background color to the color specified by three floats between 0 and 1. 34 | 35 | ### file.setIconSize(size) 36 | 37 | Set the size of all icons in the folder to `size`. 38 | 39 | ### file.setIconPos(name, x, y) 40 | 41 | Position a file icon for file named `name` at `x, y`. 42 | 43 | ### file.setWindowPos(x, y) 44 | 45 | Set the Finder window position to `x, y`. 46 | 47 | ### file.setWindowSize(w, h) 48 | 49 | Set the Finder window size to `w, h`. 50 | 51 | ### file.vSrn(value) 52 | 53 | Set the `vSrn` value to either `0` or `1`. 54 | 55 | Effect currently unknown. 56 | 57 | ### file.write(path, cb) 58 | 59 | Write the `.DS_Store` information to file at `path`. 60 | 61 | `cb` will get called with `err` upon file creation. 62 | 63 | ## Future 64 | 65 | I have started work on a Buddy Allocator and B-Tree implementation, 66 | but there is still lots of work required. Having theese would make 67 | it easy to both read and manipulate files. It also wouldn't require 68 | shipping a `DSStore-clean` file. 69 | 70 | ## Thanks 71 | 72 | A special thanks to Wim Lewis who have written a complete implementation 73 | in perl. His documentation of the file format helped me very much. 74 | 75 | http://search.cpan.org/~wiml/Mac-Finder-DSStore/DSStoreFormat.pod 76 | -------------------------------------------------------------------------------- /assets/DSStore-clean: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/node-ds-store/75b91d6cb6ee9b20012f3aae0dcff7f4ff990d12/assets/DSStore-clean -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var alias = require('macos-alias') 3 | var util = require('util') 4 | 5 | var Entry = require('./lib/entry') 6 | var DSStore = require('./lib/ds-store') 7 | 8 | function Helper () { 9 | this.file = new DSStore() 10 | this.opts = { 11 | window: { x: 100, y: 100 } 12 | } 13 | } 14 | 15 | Helper.prototype.setBackgroundPath = function (path) { 16 | this.opts.backgroundPath = path 17 | } 18 | 19 | Helper.prototype.setBackgroundColor = function (red, green, blue) { 20 | this.opts.backgroundColor = [red, green, blue] 21 | } 22 | 23 | Helper.prototype.setIconSize = function (size) { 24 | this.opts.iconSize = size 25 | } 26 | 27 | Helper.prototype.setIconPos = function (name, x, y) { 28 | this.file.push(Entry.construct(name, 'Iloc', { x: x, y: y })) 29 | } 30 | 31 | Helper.prototype.setWindowPos = function (x, y) { 32 | this.opts.window.x = x 33 | this.opts.window.y = y 34 | } 35 | 36 | Helper.prototype.setWindowSize = function (w, h) { 37 | this.opts.window.width = w 38 | this.opts.window.height = h + 22 39 | } 40 | 41 | Helper.prototype.vSrn = function (value) { 42 | assert(value === 0 || value === 1) 43 | this.file.push(Entry.construct('.', 'vSrn', { value: value })) 44 | } 45 | 46 | Helper.prototype.write = function (path, cb) { 47 | var rawAlias, colorComponents 48 | 49 | if (this.opts.backgroundPath) { 50 | rawAlias = alias.create(this.opts.backgroundPath) 51 | } 52 | 53 | if (this.opts.backgroundColor) { 54 | colorComponents = this.opts.backgroundColor 55 | } 56 | 57 | this.file.push(Entry.construct('.', 'bwsp', this.opts.window)) 58 | this.file.push(Entry.construct('.', 'icvp', { iconSize: this.opts.iconSize, rawAlias: rawAlias, colorComponents: colorComponents })) 59 | 60 | this.file.write(path, cb) 61 | } 62 | 63 | /* Backwards compatibility */ 64 | Helper.prototype.setBackground = util.deprecate( 65 | Helper.prototype.setBackgroundPath, 66 | 'setBackground is deprecated, please use setBackgroundPath' 67 | ) 68 | 69 | module.exports = exports = Helper 70 | -------------------------------------------------------------------------------- /lib/buddy-allocator.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | 3 | function BuddyAllocator () { 4 | this.offsets = new Array(0) 5 | this.freelist = new Array(32) 6 | 7 | for (var i = 0; i < 32; i++) { 8 | this.freelist[i] = [] 9 | } 10 | 11 | this.freelist[31].push(0) 12 | 13 | var head = this._alloc(5) 14 | assert(head === 0) 15 | } 16 | 17 | BuddyAllocator.prototype._alloc = function (width) { 18 | assert(width < 32) 19 | 20 | var list = this.freelist[width] 21 | 22 | if (list.length > 0) { 23 | // There is a block of the desired size; return it. 24 | 25 | return list.shift() 26 | } else { 27 | // Allocate a block of the next larger size; split 28 | // it and put the other half on the free list. 29 | 30 | var offset = this._alloc(width + 1) 31 | var buddy = offset ^ Math.pow(2, width) 32 | 33 | this._free(buddy, width) 34 | return offset 35 | } 36 | } 37 | 38 | BuddyAllocator.prototype._free = function (offset, width) { 39 | var list = this.freelist[width] 40 | var buddy = offset ^ Math.pow(2, width) 41 | 42 | var idx = list.indexOf(buddy) 43 | 44 | if (~idx) { 45 | // Our buddy is free. Coalesce, and 46 | // add the coalesced block to freelist. 47 | 48 | list.splice(idx, 1) 49 | this._free(offset & buddy, width + 1) 50 | } else { 51 | // Add this block to the freelist 52 | 53 | list.push(offset) 54 | // FIXME: maybe sort the list as well 55 | } 56 | } 57 | 58 | BuddyAllocator.prototype.allocate = function (bytes, blocknum) { 59 | if (blocknum === undefined) { 60 | blocknum = 1 61 | 62 | while (this.offsets[blocknum] !== undefined) { 63 | blocknum += 1 64 | } 65 | } 66 | 67 | var wantwidth = 5 68 | while (bytes > (1 << wantwidth)) { 69 | wantwidth += 1 70 | } 71 | 72 | var blockaddr, blockwidth, blockoffset 73 | if (this.offsets[blocknum]) { 74 | blockaddr = this.offsets[blocknum] 75 | blockwidth = blockaddr & 0x1F 76 | blockoffset = blockaddr & ~0x1F 77 | if (blockwidth === wantwidth) { 78 | return blocknum 79 | } else { 80 | this._free(blockoffset, blockwidth) 81 | delete this.offsets[blocknum] 82 | } 83 | } 84 | 85 | blockoffset = this._alloc(wantwidth) 86 | blockaddr = blockoffset | wantwidth 87 | this.offsets[blocknum] = blockaddr 88 | 89 | return blocknum 90 | } 91 | 92 | module.exports = exports = BuddyAllocator 93 | -------------------------------------------------------------------------------- /lib/ds-store.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | // var assert = require('assert') 4 | 5 | var Entry = require('./entry') 6 | // var partition = require('./partition') 7 | // var BuddyAllocator = require('./buddy-allocator') 8 | 9 | function DSStore () { 10 | this.entries = [] 11 | // this.store = new BuddyAllocator() 12 | } 13 | 14 | DSStore.prototype.push = function (entry) { 15 | this.entries.push(entry) 16 | } 17 | 18 | // DSStore.prototype._header = function (offset, size) { 19 | // var header = new Buffer(32) 20 | // 21 | // // header.writeUInt32BE(1, 0) Appears before header, eg. not part of it 22 | // header.write('Bud1', 0, 'ascii') 23 | // 24 | // header.writeUInt32BE(offset, 4) 25 | // header.writeUInt32BE(size, 8) 26 | // header.writeUInt32BE(offset, 12) 27 | // 28 | // header.writeUInt32BE(0x100C, 16) 29 | // header.writeUInt32BE(0x0000, 20) // 0x0087 30 | // header.writeUInt32BE(0x0000, 24) // 0x200B 31 | // header.writeUInt32BE(0x0000, 28) 32 | // 33 | // return header 34 | // } 35 | // 36 | // DSStore.prototype._entries = function () { 37 | // var tocblock 38 | // var pagesize = 0x1000 39 | // 40 | // if ('DSDB' in this.store.toc) { 41 | // throw new Error('Not implemented') 42 | // } 43 | // 44 | // tocblock = this.store.allocate(20) 45 | // this.store.toc['DSDB'] = tocblock 46 | // 47 | // var pagecount, reccount, height, children 48 | // 49 | // reccount = this.entries.length 50 | // pagecount = 0 51 | // height = 0 52 | // children = [] 53 | // 54 | // do { 55 | // var sizes 56 | // 57 | // if (children.length > 0) { 58 | // sizes = this.entries.map(function (e) { return 4 + e.length() }) 59 | // } else { 60 | // sizes = this.entries.map(function (e) { return e.length() }) 61 | // } 62 | // 63 | // var interleaf = partition.sizes(pagesize - 8, sizes) 64 | // var nchildren = [] 65 | // var next = 0 66 | // 67 | // throw new Error('Not implemented') 68 | // } while (children.length > 1) 69 | // } 70 | 71 | // sub putDSDBEntries { 72 | // my(@children); 73 | 74 | // # Partition the records into btree nodes, from the bottom of 75 | // # the tree working towards the root. 76 | // do { 77 | // my(@sizes); 78 | 79 | // if (@children) { 80 | // # Interior node: child pointers interleaved with records 81 | // @sizes = map { 4 + $_->byteSize } @$recs; 82 | // } else { 83 | // # Leaf node: just a bunch of records 84 | // @sizes = map { $_->byteSize } @$recs; 85 | // } 86 | 87 | // # In addition to @sizes, each page contains a record 88 | // # count and a flag/childnode field (4 bytes each) 89 | // my(@interleaf) = &partition_sizes($pagesize - 8, @sizes); 90 | // my(@nchildren); 91 | 92 | // my($next) = 0; 93 | // foreach my $non (@interleaf, 1+$#$recs) { 94 | // my($blknr) = $file->allocate($pagesize); 95 | // push(@nchildren, $blknr); 96 | // my($blk) = $file->blockByNumber($blknr, 1); 97 | // if (@children) { 98 | // &writeBTreeNode($blk, 99 | // [ @$recs[ $next .. $non-1 ] ], 100 | // [ @children[ $next .. $non ] ] ); 101 | // } else { 102 | // &writeBTreeNode($blk, 103 | // [ @$recs[ $next .. $non-1 ] ]); 104 | // } 105 | // $blk->close(1); 106 | // $next = $non + 1; 107 | // $pagecount ++; 108 | // } 109 | 110 | // $height ++; 111 | // $recs = [ map { $recs->[$_] } @interleaf ]; 112 | // @children = @nchildren; 113 | // die unless @children == 1+@$recs; 114 | // } while(@children > 1); 115 | // die unless 0 == @$recs; 116 | 117 | // my($masterblock) = $file->blockByNumber($tocblock, 1); 118 | // $masterblock->write('NNNNN', 119 | // $children[0], 120 | // $height - 1, 121 | // $reccount, 122 | // $pagecount, 123 | // $pagesize); 124 | // $masterblock->close; 125 | 126 | // 1; 127 | // } 128 | // 129 | // DSStore.prototype.__REAL__write = function (filePath) { 130 | // var store = new Buffer(15360) 131 | // var offset = 8192 132 | // var size = 2048 133 | // var currentPos = 0 134 | // 135 | // store.fill(0) 136 | // 137 | // this._header(offset, size).copy(store, currentPos) 138 | // currentPos += 32 139 | // 140 | // var blockAddresses = [ 141 | // 0x0000200B, 142 | // 0x00000045, 143 | // 0x0000100C 144 | // ] 145 | // 146 | // currentPos = offset 147 | // store.writeUInt32BE(blockAddresses.length, currentPos) 148 | // store.writeUInt32BE(0, currentPos + 4) 149 | // 150 | // currentPos += 8 151 | // 152 | // store.fill(0, currentPos, currentPos + (256 * 4)) 153 | // 154 | // blockAddresses.forEach(function (e, i) { 155 | // store.writeUInt32BE(e, currentPos + (i * 4)) 156 | // }) 157 | // 158 | // currentPos += (256 * 4) 159 | // 160 | // var directoryEntries = [ 161 | // 'DSDB' 162 | // ] 163 | // 164 | // store.writeUInt32BE(directoryEntries.length, currentPos) 165 | // currentPos += 4 166 | // 167 | // directoryEntries.forEach(function (e, i) { 168 | // var b = new Buffer(e, 'ascii') 169 | // store.writeUInt8(b.length, currentPos) 170 | // b.copy(store, currentPos + 1) 171 | // store.writeUInt32BE(i + 1, currentPos + 1 + b.length) 172 | // currentPos += 1 + b.length + 4 173 | // }) 174 | // 175 | // var freeList = [ 176 | // [], 177 | // [], 178 | // [], 179 | // [], 180 | // [], 181 | // [32, 96], 182 | // [], 183 | // [128], 184 | // [256], 185 | // [512], 186 | // [1024], 187 | // [2048, 10240], 188 | // [12288], 189 | // [], 190 | // [16384], 191 | // [32768], 192 | // [65536], 193 | // [131072], 194 | // [262144], 195 | // [524288], 196 | // [1048576], 197 | // [2097152], 198 | // [4194304], 199 | // [8388608], 200 | // [16777216], 201 | // [33554432], 202 | // [67108864], 203 | // [134217728], 204 | // [268435456], 205 | // [536870912], 206 | // [1073741824], 207 | // [] 208 | // ] 209 | // 210 | // assert(freeList.length === 32) 211 | // assert(freeList[31].length === 0) 212 | // 213 | // freeList.forEach(function (e) { 214 | // store.writeUInt32BE(e.length, currentPos) 215 | // e.forEach(function (e, i) { 216 | // store.writeUInt32BE(e, currentPos + 4 + (i * 4)) 217 | // }) 218 | // currentPos += 4 + (e.length * 4) 219 | // }) 220 | // 221 | // /* 222 | // * Maybe jump to blockAddresses[0] (+- 4/8 bytes) and write something like: 223 | // * 224 | // * 00 00 20 0B 225 | // * 00 00 00 45 226 | // * 00 00 10 0C 227 | // * 00 00 00 00 228 | // * 229 | // */ 230 | // 231 | // // 232 | // 233 | // var entries = this.entries.sort(Entry.sort); 234 | // 235 | // // should have something to do with blockAddresses[2] 236 | // [4096].forEach(function (e) { 237 | // currentPos = e 238 | // 239 | // var P = 0 240 | // var count = entries.length 241 | // 242 | // store.writeUInt32BE(P, currentPos) 243 | // store.writeUInt32BE(count, currentPos + 4) 244 | // currentPos += 8 245 | // 246 | // entries.forEach(function (e, i) { 247 | // e.buffer.copy(store, currentPos) 248 | // currentPos += e.buffer.length 249 | // }) 250 | // }) 251 | // 252 | // // 253 | // 254 | // var out = fs.createWriteStream(filePath) 255 | // 256 | // out.write(new Buffer('00000001', 'hex')) 257 | // out.write(store) 258 | // 259 | // out.end() 260 | // } 261 | 262 | DSStore.prototype.write = function (filePath, cb) { 263 | var entries = this.entries.sort(Entry.sort) 264 | 265 | fs.readFile(path.join(__dirname, '/../assets/DSStore-clean'), function (err, buf) { 266 | if (err) return cb(err) 267 | 268 | var modified = new Buffer(3840) 269 | 270 | modified.fill(0) 271 | 272 | var currentPos = 0 273 | 274 | var P = 0 275 | var count = entries.length 276 | 277 | modified.writeUInt32BE(P, currentPos) 278 | modified.writeUInt32BE(count, currentPos + 4) 279 | currentPos += 8 280 | 281 | entries.forEach(function (e, i) { 282 | var b = e.buffer 283 | b.copy(modified, currentPos) 284 | currentPos += b.length 285 | }) 286 | 287 | buf.writeUInt32BE(entries.length, 76) 288 | modified.copy(buf, 4100) 289 | 290 | fs.writeFile(filePath, buf, function (err) { 291 | cb(err) 292 | }) 293 | }) 294 | } 295 | 296 | module.exports = exports = DSStore 297 | -------------------------------------------------------------------------------- /lib/entry.js: -------------------------------------------------------------------------------- 1 | var tn1150 = require('tn1150') 2 | var bplist = require('bplist-creator') 3 | 4 | function utf16be (str) { 5 | var b = new Buffer(str, 'ucs2') 6 | 7 | for (var i = 0; i < b.length; i += 2) { 8 | var a = b[i] 9 | b[i] = b[i + 1] 10 | b[i + 1] = a 11 | } 12 | 13 | return b 14 | } 15 | 16 | function Entry (filename, structureId, dataType, blob) { 17 | this.filename = tn1150.normalize(filename) 18 | this.structureId = structureId 19 | 20 | var filenameLength = this.filename.length 21 | var filenameBytes = filenameLength * 2 22 | 23 | this.buffer = new Buffer(4 + filenameBytes + 4 + 4 + blob.length) 24 | 25 | this.buffer.writeUInt32BE(filenameLength, 0) 26 | utf16be(this.filename).copy(this.buffer, 4) 27 | this.buffer.write(structureId, 4 + filenameBytes, 'ascii') 28 | this.buffer.write(dataType, 8 + filenameBytes, 'ascii') 29 | 30 | blob.copy(this.buffer, 12 + filenameBytes) 31 | } 32 | 33 | Entry.prototype.length = function () { 34 | return this.buffer.length() 35 | } 36 | 37 | Entry.sort = function (a, b) { 38 | var s1 = tn1150.compare(a.filename, b.filename) 39 | var s2 = a.structureId.localeCompare(b.structureId) 40 | return s1 || s2 41 | } 42 | 43 | Entry.construct = function (filename, structureId, opts) { 44 | var dataType, blob 45 | 46 | var opt = function (key, def) { 47 | if (key in opts) { 48 | return opts[key] 49 | } else if (def === undefined) { 50 | throw new TypeError('Missing option: ' + key) 51 | } else { 52 | return def 53 | } 54 | } 55 | 56 | switch (structureId) { 57 | case 'BKGD': 58 | 59 | dataType = 'blob' 60 | blob = new Buffer(12 + 4) 61 | blob.writeUInt32BE(blob.length - 4, 0) 62 | 63 | if (opts.color) { 64 | blob.write('ClrB', 4, 'ascii') 65 | throw new Error('Not implemented') 66 | } else if (opts.pictureByteLength) { 67 | blob.write('PctB', 4, 'ascii') 68 | blob.writeUInt32BE(opts.pictureByteLength, 8) 69 | } else { 70 | blob.write('DefB', 4, 'ascii') 71 | } 72 | 73 | break 74 | case 'Iloc': 75 | 76 | dataType = 'blob' 77 | blob = new Buffer(16 + 4) 78 | blob.writeUInt32BE(blob.length - 4, 0) 79 | 80 | blob.writeUInt32BE(opts.x, 4) 81 | blob.writeUInt32BE(opts.y, 8) 82 | 83 | blob.write('FFFFFF00', 12, 'hex') 84 | 85 | break 86 | case 'fwi0': 87 | 88 | throw new Error('Deprecated: Use `bwsp` (I think this is for old OS X)') 89 | 90 | // dataType = 'blob' 91 | // blob = new Buffer(16 + 4) 92 | // blob.writeUInt32BE(blob.length - 4, 0) 93 | // 94 | // blob.writeUInt16BE(opts.top, 4) 95 | // blob.writeUInt16BE(opts.left, 6) 96 | // blob.writeUInt16BE(opts.bottom, 8) 97 | // blob.writeUInt16BE(opts.right, 10) 98 | // 99 | // blob.write(opts.view || 'icnv', 12, 'ascii') 100 | // blob.write('00000000', 16, 'hex') 101 | // 102 | // break 103 | case 'pict': 104 | 105 | // Create an alias with `opts.picturePath` 106 | 107 | throw new Error('Not implemented') 108 | 109 | // break 110 | case 'bwsp': 111 | 112 | dataType = 'bplist' 113 | blob = bplist({ 114 | ContainerShowSidebar: true, 115 | ShowPathbar: false, 116 | ShowSidebar: true, 117 | ShowStatusBar: false, 118 | ShowTabView: false, 119 | ShowToolbar: false, 120 | SidebarWidth: 0, 121 | WindowBounds: 122 | '{{' + opt('x') + ', ' + opt('y') + '},' + 123 | ' {' + opt('width') + ', ' + opt('height') + '}}' 124 | }) 125 | 126 | break 127 | case 'icvp': 128 | 129 | var plistObj = { 130 | backgroundType: 1, 131 | backgroundColorRed: new bplist.Real(1), 132 | backgroundColorGreen: new bplist.Real(1), 133 | backgroundColorBlue: new bplist.Real(1), 134 | showIconPreview: true, 135 | showItemInfo: false, 136 | textSize: new bplist.Real(12), 137 | iconSize: new bplist.Real(opt('iconSize')), 138 | viewOptionsVersion: 1, 139 | gridSpacing: new bplist.Real(100), 140 | gridOffsetX: new bplist.Real(0), 141 | gridOffsetY: new bplist.Real(0), 142 | labelOnBottom: true, 143 | arrangeBy: 'none' 144 | } 145 | 146 | if (opts.colorComponents) { 147 | plistObj.backgroundColorRed = new bplist.Real(opts.colorComponents[0]) 148 | plistObj.backgroundColorGreen = new bplist.Real(opts.colorComponents[1]) 149 | plistObj.backgroundColorBlue = new bplist.Real(opts.colorComponents[2]) 150 | } 151 | 152 | if (opts.rawAlias) { 153 | plistObj.backgroundType = 2 154 | plistObj.backgroundImageAlias = opts.rawAlias 155 | } 156 | 157 | dataType = 'bplist' 158 | blob = bplist(plistObj) 159 | 160 | break 161 | case 'vSrn': 162 | 163 | dataType = 'long' 164 | blob = new Buffer(4) 165 | 166 | blob.writeUInt32BE(opt('value'), 0) 167 | 168 | break 169 | default: 170 | throw new Error('Not implemented') 171 | } 172 | 173 | if (dataType === 'bplist') { 174 | dataType = 'blob' 175 | var buf = blob 176 | 177 | blob = new Buffer(buf.length + 4) 178 | blob.writeUInt32BE(buf.length, 0) 179 | buf.copy(blob, 4) 180 | } 181 | 182 | return new Entry(filename, structureId, dataType, blob) 183 | } 184 | 185 | module.exports = exports = Entry 186 | -------------------------------------------------------------------------------- /lib/partition.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | 3 | exports.sizes = function (max, sizes) { 4 | assert(Array.isArray(sizes)) 5 | 6 | var sum = sizes.reduce(function (p, c) { 7 | return p + c 8 | }, 0) 9 | 10 | assert(sum > max) 11 | 12 | var ejecta = [] 13 | var bcount = Math.ceil(sum, max) 14 | var target = sum / bcount 15 | 16 | while (true) { 17 | var n = 0 18 | var bsum = 0 19 | 20 | while (n < sizes.length && bsum < target && (bsum + sizes[n]) < max) { 21 | bsum += sizes[n] 22 | n += 1 23 | } 24 | 25 | if (n >= sizes.length) { 26 | break 27 | } 28 | 29 | ejecta.push(n) 30 | n += 1 31 | } 32 | 33 | return ejecta 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ds-store", 3 | "version": "0.1.6", 4 | "license": "MIT", 5 | "main": "index.js", 6 | "author": "Linus Unnebäck ", 7 | "scripts": { 8 | "test": "standard" 9 | }, 10 | "dependencies": { 11 | "bplist-creator": "~0.0.3", 12 | "macos-alias": "~0.2.5", 13 | "tn1150": "^0.1.0" 14 | }, 15 | "contributors": [ 16 | "Linus Unnebäck ", 17 | "Davide Liessi " 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "http://github.com/LinusU/node-ds-store.git" 22 | }, 23 | "devDependencies": { 24 | "standard": "^7.0.1" 25 | } 26 | } 27 | --------------------------------------------------------------------------------