Merge branch 'master' of git://github.com/angavrilov/dfhack
						commit
						9148079745
					
				
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,314 @@ | ||||
| /*
 | ||||
| https://github.com/peterix/dfhack
 | ||||
| Copyright (c) 2011 Petr Mrázek <peterix@gmail.com> | ||||
| 
 | ||||
| A thread-safe logging console with a line editor for windows. | ||||
| 
 | ||||
| Based on linenoise win32 port, | ||||
| copyright 2010, Jon Griffiths <jon_p_griffiths at yahoo dot com>. | ||||
| All rights reserved. | ||||
| Based on linenoise, copyright 2010, Salvatore Sanfilippo <antirez at gmail dot com>. | ||||
| The original linenoise can be found at: http://github.com/antirez/linenoise
 | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
| 
 | ||||
|   * Redistributions of source code must retain the above copyright notice, | ||||
|     this list of conditions and the following disclaimer. | ||||
|   * Redistributions in binary form must reproduce the above copyright | ||||
|     notice, this list of conditions and the following disclaimer in the | ||||
|     documentation and/or other materials provided with the distribution. | ||||
|   * Neither the name of Redis nor the names of its contributors may be used | ||||
|     to endorse or promote products derived from this software without | ||||
|     specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
| POSSIBILITY OF SUCH DAMAGE. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include <stdarg.h> | ||||
| #include <errno.h> | ||||
| #include <stdio.h> | ||||
| #include <assert.h> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <istream> | ||||
| #include <string> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #include <cstdio> | ||||
| #include <cstdlib> | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <md5wrapper.h> | ||||
| 
 | ||||
| using std::cout; | ||||
| using std::cerr; | ||||
| using std::endl; | ||||
| 
 | ||||
| typedef unsigned char patch_byte; | ||||
| 
 | ||||
| struct BinaryPatch { | ||||
|     struct Byte { | ||||
|         unsigned offset; | ||||
|         patch_byte old_val, new_val; | ||||
|     }; | ||||
|     enum State { | ||||
|         Conflict = 0, | ||||
|         Unapplied = 1, | ||||
|         Applied = 2, | ||||
|         Partial = 3 | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<Byte> entries; | ||||
| 
 | ||||
|     bool loadDIF(std::string name); | ||||
|     State checkState(const patch_byte *ptr, size_t len); | ||||
| 
 | ||||
|     void apply(patch_byte *ptr, size_t len, bool newv); | ||||
| }; | ||||
| 
 | ||||
| inline bool is_hex(char c) | ||||
| { | ||||
|     return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); | ||||
| } | ||||
| 
 | ||||
| bool BinaryPatch::loadDIF(std::string name) | ||||
| { | ||||
|     entries.clear(); | ||||
| 
 | ||||
|     std::ifstream infile(name); | ||||
|     if(infile.bad()) | ||||
|     { | ||||
|         cerr << "Cannot open file: " << name << endl; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     std::string s; | ||||
|     while(std::getline(infile, s)) | ||||
|     { | ||||
|         // Parse lines that begin with "[0-9a-f]+:"
 | ||||
|         size_t idx = s.find(':'); | ||||
|         if (idx == std::string::npos || idx == 0 || idx > 8) | ||||
|             continue; | ||||
| 
 | ||||
|         bool ok = true; | ||||
|         for (size_t i = 0; i < idx; i++) | ||||
|             if (!is_hex(s[i])) | ||||
|                 ok = false; | ||||
|         if (!ok) | ||||
|             continue; | ||||
| 
 | ||||
|         unsigned off, oval, nval; | ||||
|         int nchar = 0; | ||||
|         int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar); | ||||
| 
 | ||||
|         if (cnt < 3) | ||||
|         { | ||||
|             cerr << "Could not parse: " << s << endl; | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         for (size_t i = nchar; i < s.size(); i++) | ||||
|         { | ||||
|             if (!isspace(s[i])) | ||||
|             { | ||||
|                 cerr << "Garbage at end of line: " << s << endl; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (oval >= 256 || nval >= 256) | ||||
|         { | ||||
|             cerr << "Invalid byte values: " << s << endl; | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         Byte bv = { off, patch_byte(oval), patch_byte(nval) }; | ||||
|         entries.push_back(bv); | ||||
|     } | ||||
| 
 | ||||
|     if (entries.empty()) | ||||
|     { | ||||
|         cerr << "No lines recognized." << endl; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len) | ||||
| { | ||||
|     int state = 0; | ||||
| 
 | ||||
|     for (size_t i = 0; i < entries.size(); i++) | ||||
|     { | ||||
|         Byte &bv = entries[i]; | ||||
| 
 | ||||
|         if (bv.offset >= len) | ||||
|         { | ||||
|             cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl; | ||||
|             return Conflict; | ||||
|         } | ||||
| 
 | ||||
|         patch_byte cv = ptr[bv.offset]; | ||||
|         if (bv.old_val == cv) | ||||
|             state |= Unapplied; | ||||
|         else if (bv.new_val == cv) | ||||
|             state |= Applied; | ||||
|         else | ||||
|         { | ||||
|             cerr << std::hex << bv.offset << ": " << bv.old_val << " " << bv.new_val | ||||
|                  << ", but currently " << cv << std::dec << endl; | ||||
|             return Conflict; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return State(state); | ||||
| } | ||||
| 
 | ||||
| void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv) | ||||
| { | ||||
|     for (size_t i = 0; i < entries.size(); i++) | ||||
|     { | ||||
|         Byte &bv = entries[i]; | ||||
|         assert (bv.offset < len); | ||||
| 
 | ||||
|         ptr[bv.offset] = (newv ? bv.new_val : bv.old_val); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool load_file(std::vector<patch_byte> *pvec, std::string fname) | ||||
| { | ||||
|     FILE *f = fopen(fname.c_str(), "rb"); | ||||
|     if (!f) | ||||
|     { | ||||
|         cerr << "Cannot open file: " << fname << endl; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fseek(f, 0, SEEK_END); | ||||
|     pvec->resize(ftell(f)); | ||||
|     fseek(f, 0, SEEK_SET); | ||||
|     size_t cnt = fread(pvec->data(), 1, pvec->size(), f); | ||||
|     fclose(f); | ||||
| 
 | ||||
|     return cnt == pvec->size(); | ||||
| } | ||||
| 
 | ||||
| bool save_file(const std::vector<patch_byte> &pvec, std::string fname) | ||||
| { | ||||
|     FILE *f = fopen(fname.c_str(), "wb"); | ||||
|     if (!f) | ||||
|     { | ||||
|         cerr << "Cannot open file: " << fname << endl; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f); | ||||
|     fclose(f); | ||||
| 
 | ||||
|     return cnt == pvec.size(); | ||||
| } | ||||
| 
 | ||||
| std::string compute_hash(const std::vector<patch_byte> &pvec) | ||||
| { | ||||
|     md5wrapper md5; | ||||
|     return md5.getHashFromBytes(pvec.data(), pvec.size()); | ||||
| } | ||||
| 
 | ||||
| int main (int argc, char *argv[]) | ||||
| { | ||||
|     if (argc <= 3) | ||||
|     { | ||||
|         cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl; | ||||
|         return 2; | ||||
|     } | ||||
| 
 | ||||
|     std::string cmd = argv[1]; | ||||
| 
 | ||||
|     if (cmd != "check" && cmd != "apply" && cmd != "remove") | ||||
|     { | ||||
|         cerr << "Invalid command: " << cmd << endl; | ||||
|         return 2; | ||||
|     } | ||||
| 
 | ||||
|     std::string exe_file = argv[2]; | ||||
|     std::vector<patch_byte> bindata; | ||||
|     if (!load_file(&bindata, exe_file)) | ||||
|         return 2; | ||||
| 
 | ||||
|     BinaryPatch patch; | ||||
|     if (!patch.loadDIF(argv[3])) | ||||
|         return 2; | ||||
| 
 | ||||
|     BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size()); | ||||
|     if (state == BinaryPatch::Conflict) | ||||
|         return 1; | ||||
| 
 | ||||
|     if (cmd == "check") | ||||
|     { | ||||
|         switch (state) | ||||
|         { | ||||
|         case BinaryPatch::Unapplied: | ||||
|             cout << "Currently not applied." << endl; | ||||
|             break; | ||||
|         case BinaryPatch::Applied: | ||||
|             cout << "Currently applied." << endl; | ||||
|             break; | ||||
|         case BinaryPatch::Partial: | ||||
|             cout << "Currently partially applied." << endl; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         return 0; | ||||
|     } | ||||
|     else if (cmd == "apply") | ||||
|     { | ||||
|         if (state == BinaryPatch::Applied) | ||||
|         { | ||||
|             cout << "Already applied." << endl; | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         patch.apply(bindata.data(), bindata.size(), true); | ||||
|     } | ||||
|     else if (cmd == "remove") | ||||
|     { | ||||
|         if (state == BinaryPatch::Unapplied) | ||||
|         { | ||||
|             cout << "Already removed." << endl; | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         patch.apply(bindata.data(), bindata.size(), false); | ||||
|     } | ||||
| 
 | ||||
|     if (!save_file(bindata, exe_file + ".bak")) | ||||
|     { | ||||
|         cerr << "Could not create backup." << endl; | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     if (!save_file(bindata, exe_file)) | ||||
|         return 1; | ||||
| 
 | ||||
|     cout << "Patched " << patch.entries.size() | ||||
|          << " bytes, new hash: " << compute_hash(bindata) << endl; | ||||
|     return 0; | ||||
| } | ||||
| @ -0,0 +1,151 @@ | ||||
| -- Support for scripted interaction sequences via coroutines. | ||||
| 
 | ||||
| local _ENV = mkmodule('gui.script') | ||||
| 
 | ||||
| local dlg = require('gui.dialogs') | ||||
| 
 | ||||
| --[[ | ||||
|   Example: | ||||
| 
 | ||||
|   start(function() | ||||
|     sleep(100, 'frames') | ||||
|     print(showYesNoPrompt('test', 'print true?')) | ||||
|   end) | ||||
| ]] | ||||
| 
 | ||||
| -- Table of running background scripts. | ||||
| if not scripts then | ||||
|     scripts = {} | ||||
|     setmetatable(scripts, { __mode = 'k' }) | ||||
| end | ||||
| 
 | ||||
| local function do_resume(inst, ...) | ||||
|     inst.gen = inst.gen + 1 | ||||
|     return (dfhack.saferesume(inst.coro, ...)) | ||||
| end | ||||
| 
 | ||||
| -- Starts a new background script by calling the function. | ||||
| function start(fn,...) | ||||
|     local coro = coroutine.create(fn) | ||||
|     local inst = { | ||||
|         coro = coro, | ||||
|         gen = 0, | ||||
|     } | ||||
|     scripts[coro] = inst | ||||
|     return do_resume(inst, ...) | ||||
| end | ||||
| 
 | ||||
| -- Checks if called from a background script | ||||
| function in_script() | ||||
|     return scripts[coroutine.running()] ~= nil | ||||
| end | ||||
| 
 | ||||
| local function getinst() | ||||
|     local inst = scripts[coroutine.running()] | ||||
|     if not inst then | ||||
|         error('Not in a gui script coroutine.') | ||||
|     end | ||||
|     return inst | ||||
| end | ||||
| 
 | ||||
| local function invoke_resume(inst,gen,quiet,...) | ||||
|     local state = coroutine.status(inst.coro) | ||||
|     if state ~= 'suspended' then | ||||
|         if state ~= 'dead' then | ||||
|             dfhack.printerr(debug.traceback('resuming a non-waiting continuation')) | ||||
|         end | ||||
|     elseif inst.gen > gen then | ||||
|         if not quiet then | ||||
|             dfhack.printerr(debug.traceback('resuming an expired continuation')) | ||||
|         end | ||||
|     else | ||||
|         do_resume(inst, ...) | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| -- Returns a callback that resumes the script from wait with given return values | ||||
| function mkresume(...) | ||||
|     local inst = getinst() | ||||
|     return curry(invoke_resume, inst, inst.gen, false, ...) | ||||
| end | ||||
| 
 | ||||
| -- Like mkresume, but does not complain if already resumed from this wait | ||||
| function qresume(...) | ||||
|     local inst = getinst() | ||||
|     return curry(invoke_resume, inst, inst.gen, true, ...) | ||||
| end | ||||
| 
 | ||||
| -- Wait until a mkresume callback is called, then return its arguments. | ||||
| -- Once it returns, all mkresume callbacks created before are invalidated. | ||||
| function wait() | ||||
|     getinst() -- check that the context is right | ||||
|     return coroutine.yield() | ||||
| end | ||||
| 
 | ||||
| -- Wraps dfhack.timeout for coroutines. | ||||
| function sleep(time, quantity) | ||||
|     if dfhack.timeout(time, quantity, mkresume()) then | ||||
|         wait() | ||||
|         return true | ||||
|     else | ||||
|         return false | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| -- Some dialog wrappers: | ||||
| 
 | ||||
| function showMessage(title, text, tcolor) | ||||
|     dlg.MessageBox{ | ||||
|         frame_title = title, | ||||
|         text = text, | ||||
|         text_pen = tcolor, | ||||
|         on_close = qresume(nil) | ||||
|     }:show() | ||||
| 
 | ||||
|     return wait() | ||||
| end | ||||
| 
 | ||||
| function showYesNoPrompt(title, text, tcolor) | ||||
|     dlg.MessageBox{ | ||||
|         frame_title = title, | ||||
|         text = text, | ||||
|         text_pen = tcolor, | ||||
|         on_accept = mkresume(true), | ||||
|         on_cancel = mkresume(false), | ||||
|         on_close = qresume(nil) | ||||
|     }:show() | ||||
| 
 | ||||
|     return wait() | ||||
| end | ||||
| 
 | ||||
| function showInputPrompt(title, text, tcolor, input, min_width) | ||||
|     dlg.InputBox{ | ||||
|         frame_title = title, | ||||
|         text = text, | ||||
|         text_pen = tcolor, | ||||
|         input = input, | ||||
|         frame_width = min_width, | ||||
|         on_input = mkresume(true), | ||||
|         on_cancel = mkresume(false), | ||||
|         on_close = qresume(nil) | ||||
|     }:show() | ||||
| 
 | ||||
|     return wait() | ||||
| end | ||||
| 
 | ||||
| function showListPrompt(title, text, tcolor, choices, min_width) | ||||
|     dlg.ListBox{ | ||||
|         frame_title = title, | ||||
|         text = text, | ||||
|         text_pen = tcolor, | ||||
|         choices = choices, | ||||
|         frame_width = min_width, | ||||
|         on_select = mkresume(true), | ||||
|         on_cancel = mkresume(false), | ||||
|         on_close = qresume(nil) | ||||
|     }:show() | ||||
| 
 | ||||
|     return wait() | ||||
| end | ||||
| 
 | ||||
| return _ENV | ||||
| @ -1 +1 @@ | ||||
| Subproject commit 2f76de54dacd32af567b177adfb9d037fdf62d9b | ||||
| Subproject commit 20c6d9c743f1c5a20bb288f427b101e9b2a138d7 | ||||
| @ -0,0 +1,22 @@ | ||||
| building_spatter | ||||
| 
 | ||||
| [OBJECT:BUILDING] | ||||
| 
 | ||||
| [BUILDING_WORKSHOP:GREASING_STATION] | ||||
| 	[NAME:Greasing Station] | ||||
| 	[NAME_COLOR:2:0:1] | ||||
| 	[DIM:1:1] | ||||
| 	[WORK_LOCATION:1:1] | ||||
| 	[BUILD_LABOR:DYER] | ||||
| 	[BUILD_KEY:CUSTOM_ALT_G] | ||||
| 	[BLOCK:1:0] | ||||
| 	[TILE:0:1:150] | ||||
| 	[COLOR:0:1:0:0:1] | ||||
| 	[TILE:1:1:150] | ||||
| 	[COLOR:1:1:MAT] | ||||
| 	[TILE:2:1:8] | ||||
| 	[COLOR:2:1:MAT] | ||||
| 	[TILE:3:1:8] | ||||
| 	[COLOR:3:1:7:5:0] | ||||
| 	[BUILD_ITEM:1:BUCKET:NONE:NONE:NONE][CAN_USE_ARTIFACT] | ||||
| 	[BUILD_ITEM:1:NONE:NONE:NONE:NONE][BUILDMAT] | ||||
		Loading…
	
		Reference in New Issue