Skip to content

Commit

Permalink
Merge branch 'master' into feature/plop
Browse files Browse the repository at this point in the history
  • Loading branch information
sebamarynissen committed Jul 13, 2020
2 parents 7130274 + ee7bee8 commit c7812c2
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 25 deletions.
43 changes: 43 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,49 @@ function factory(program) {
api.refs(opts);
});

// Command for switching the active tilesets in a city.
program
.command('tracts <city>')
.option('-t, --tilesets <tilesets>', 'The tileset identifiers, given as numbers')
.option('-y, --years <years>', 'The amount of years in the cycle')
.option('--force', 'Force override the city')
.description('Changes the active tilesets in the given city')
.action(async function(city) {
let dir = this.cwd;
let file = path.resolve(dir, city);
let buff = fs.readFileSync(file);
let dbpf = new DBPF(buff);

let entry = dbpf.entries.find(entry => entry.type === FileType.TractDeveloper);
let tracts = entry.read();
if (!this.tilesets) {
throw new Error('No tilesets specified!');
}

// Parse the tilesets.
let styles = [];
for (let style of this.tilesets.split(',')) {
styles.push(+style);
}
tracts.styles = styles;

if (this.years) {
tracts.years = +this.years;
}

// Save.
let opts = baseOptions();
let output = file;
if (!this.force) {
let dir = path.dirname(file);
let name = 'TRACTS_'+path.basename(file);
output = path.join(dir, name);
}
opts.info(`Saving to ${ output }`);
await dbpf.save({ file: output });

});

// End of factory function.
return program;

Expand Down
1 change: 1 addition & 0 deletions lib/dbpf.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ DBPF.register([
require('./lot-developer-file'),
require('./com-serializer-file'),
require('./zone-manager.js'),
require('./tract-developer.js'),
]);
DBPF.register(FileType.Cohort, Exemplar);

Expand Down
49 changes: 25 additions & 24 deletions lib/file-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,35 @@
// RUL TypeID=0A5BCF4B
// Cohort TypeID=05342861
const TYPES = module.exports = {
"Exemplar": 0x6534284A,
"Cohort": 0x05342861,
"DIR": 0xE86B1EEF,
"PNG": 0x856DDBAC,

Exemplar: 0x6534284A,
Cohort: 0x05342861,
DIR: 0xE86B1EEF,
PNG: 0x856DDBAC,

// Savegame-specific files.
"LotFile": 0xC9BD5D4A,
"BuildingFile": 0xa9bd882d,
"PropFile": 0x2977AA47,
"FloraFile": 0xa9c05c85,
"BaseTextureFile": 0xc97f987c,
"OccupantFile": 0xa9bc9ab6,
"ItemIndexFile": 0x098F964D,
"RegionViewFile": 0xCA027EDB,
"ZoneDeveloperFile": 0x498f9b01,
"LotDeveloperFile": 0xa990bfe0,
"PropDeveloperFile": 0x89c48f47,
"COMSerializerFile": 0x499b23fe,
"NetworkFile": 0xc9c05c6e,
"ZoneManager": 0x298f9b2d,
LotFile: 0xC9BD5D4A,
BuildingFile: 0xa9bd882d,
PropFile: 0x2977AA47,
FloraFile: 0xa9c05c85,
BaseTextureFile: 0xc97f987c,
OccupantFile: 0xa9bc9ab6,
ItemIndexFile: 0x098F964D,
RegionViewFile: 0xCA027EDB,
ZoneDeveloperFile: 0x498f9b01,
LotDeveloperFile: 0xa990bfe0,
PropDeveloperFile: 0x89c48f47,
COMSerializerFile: 0x499b23fe,
NetworkFile: 0xc9c05c6e,
TractDeveloper: 0x2990c142,

// Sim grids
"SimGridFloat32": 0x49b9e60a,
"SimGridUint32": 0x49b9e606,
"SimGridSint16": 0x49b9e605,
"SimGridUint16": 0x49b9e604,
"SimGridSint8": 0x49b9e603,
"SimGridUint8": 0x49b9e602,
SimGridFloat32: 0x49b9e60a,
SimGridUint32: 0x49b9e606,
SimGridSint16: 0x49b9e605,
SimGridUint16: 0x49b9e604,
SimGridSint8: 0x49b9e603,
SimGridUint8: 0x49b9e602,

};

Expand Down
81 changes: 81 additions & 0 deletions lib/tract-developer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// # tract-developer.js
const Stream = require('./stream.js');
const WriteStream = require('./write-stream.js');
const Type = require('./type.js');
const { FileType } = require('./enums.js');
const crc32 = require('./crc.js');

// # TractDeveloper
// See https://community.simtropolis.com/forums/topic/758810-partial-
// mythbusting-building-style-tilesets-not-locked-down-in-the-exe/?tab=comments
// #comment-1724876. The TractDeveloper file holds which tilesets are
// currently active in the city.
class TractDeveloper extends Type(FileType.TractDeveloper) {

// ## constructor()
constructor() {
super();
this.crc = 0x00000000;
this.mem = 0x00000000;
this.u1 = 0x0003;
this.u2 = 0x0000;
this.u3 = 0x0000;
this.u4 = 0x0000;
this.u5 = 0x0000;
this.u6 = 0x01;
this.styles = [];
this.years = 5;
}

// ## parse(buff)
parse(buff) {
let rs = new Stream(buff);
let size = rs.dword();
this.crc = rs.dword();
this.mem = rs.dword();
this.u1 = rs.word();
this.u2 = rs.word();
this.u3 = rs.word();
this.u4 = rs.word();
this.u5 = rs.word();
this.u6 = rs.byte();
const length = rs.dword();
let styles = this.styles = [];
for (let i = 0; i < length; i++) {
styles.push(rs.dword());
}
this.years = rs.dword();
return this;
}

// ## *bgen()
*bgen() {
let size = 31 + 4*this.styles.length;
let buff = Buffer.allocUnsafe(size);
let ws = new WriteStream(buff);

// Size & crc are for later. Start at offset 8.
ws.jump(8);
ws.dword(this.mem);
ws.word(this.u1);
ws.word(this.u2);
ws.word(this.u3);
ws.word(this.u4);
ws.word(this.u5);
ws.byte(this.u6);
ws.dword(this.styles.length);
for (let style of this.styles) {
ws.dword(style);
}
ws.dword(this.years);

// Calculate CRC & yield.
buff.writeUInt32LE(buff.byteLength, 0);
buff.writeUInt32LE(this.crc = crc32(buff, 8), 4);

yield buff;

}

}
module.exports = TractDeveloper;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sc4",
"version": "0.0.16-alpha",
"version": "0.0.16",
"description": "A command line utility for automating SimCity 4 modding tasks & modifying savegames",
"main": "lib/index.js",
"scripts": {
Expand Down
25 changes: 25 additions & 0 deletions test/tract-developer-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// # tract-developer-test.js
const fs = require('fs');
const path = require('path');
const { expect } = require('chai');
const DBPF = require('../lib/dbpf.js');
const { FileType } = require('../lib/enums.js');

describe('The tract developer file', function() {

it('should be parsed & serialized correctly', function() {

let file = path.resolve(__dirname, 'files/city.sc4');
let buff = fs.readFileSync(file);
let dbpf = new DBPF(buff);

let entry = dbpf.entries.find(x => x.type === FileType.TractDeveloper);
let tract = entry.read();

let source = entry.decompress();
let check = tract.toBuffer();
expect(check.toString('hex')).to.equal(source.toString('hex'));

});

});

0 comments on commit c7812c2

Please sign in to comment.