From 1dd4cc56670819e72d05c306d4f97d9b5a15cd3b Mon Sep 17 00:00:00 2001 From: Timothy Collett Date: Fri, 25 May 2012 14:28:59 -0400 Subject: [PATCH] More work on getting dfhack building & compiling on Mac OS X --- library/CMakeLists.txt | 10 + library/Console-darwin.cpp | 773 ++++++++++++++++++++++++++++++++++++ library/Hooks-darwin.cpp | 169 ++++++++ library/PlugLoad-darwin.cpp | 44 ++ library/Process-darwin.cpp | 229 +++++++++++ library/include/Core.h | 8 +- library/include/Hooks.h | 8 +- 7 files changed, 1233 insertions(+), 8 deletions(-) create mode 100644 library/Console-darwin.cpp create mode 100644 library/Hooks-darwin.cpp create mode 100644 library/PlugLoad-darwin.cpp create mode 100644 library/Process-darwin.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index bcb453a99..4b7ffa7d1 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -89,6 +89,14 @@ PlugLoad-linux.cpp Process-linux.cpp ) +SET(MAIN_SOURCES_DARWIN +Console-darwin.cpp +Hooks-darwin.cpp +PlugLoad-darwin.cpp +Process-darwin.cpp +#MacPool.m +) + SET(MAIN_SOURCES_LINUX_EGGY Console-linux.cpp Hooks-egg.cpp @@ -155,6 +163,8 @@ IF(UNIX) OPTION(BUILD_EGGY "Make DFHack strangely egg-shaped." OFF) IF(BUILD_EGGY) LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_LINUX_EGGY}) + ELSEIF(APPLE) + LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_DARWIN}) ELSE() LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_LINUX}) ENDIF() diff --git a/library/Console-darwin.cpp b/library/Console-darwin.cpp new file mode 100644 index 000000000..c547f8413 --- /dev/null +++ b/library/Console-darwin.cpp @@ -0,0 +1,773 @@ +/* +https://github.com/peterix/dfhack + +A thread-safe logging console with a line editor. + +Based on linenoise: +linenoise -- guerrilla line editing library against the idea that a +line editing lib needs to be 20,000 lines of C code. + +You can find the latest source code at: + + http://github.com/antirez/linenoise + +Does a number of crazy assumptions that happen to be true in 99.9999% of +the 2010 UNIX computers around. + +------------------------------------------------------------------------ + +Copyright (c) 2010, Salvatore Sanfilippo +Copyright (c) 2010, Pieter Noordhuis +Copyright (c) 2011, Petr Mrázek + +All rights reserved. + +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. + +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 +HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// George Vulov for MacOSX +#ifndef __LINUX__ +#define TEMP_FAILURE_RETRY(expr) \ + ({ long int _res; \ + do _res = (long int) (expr); \ + while (_res == -1L && errno == EINTR); \ + _res; }) +#endif + +#include "Console.h" +#include "Hooks.h" +using namespace DFHack; + +#include "tinythread.h" +using namespace tthread; + +static int isUnsupportedTerm(void) +{ + static const char *unsupported_term[] = {"dumb","cons25",NULL}; + char *term = getenv("TERM"); + int j; + + if (term == NULL) return 0; + for (j = 0; unsupported_term[j]; j++) + if (!strcasecmp(term,unsupported_term[j])) return 1; + return 0; +} + +const char * ANSI_CLS = "\033[2J"; +const char * ANSI_BLACK = "\033[22;30m"; +const char * ANSI_RED = "\033[22;31m"; +const char * ANSI_GREEN = "\033[22;32m"; +const char * ANSI_BROWN = "\033[22;33m"; +const char * ANSI_BLUE = "\033[22;34m"; +const char * ANSI_MAGENTA = "\033[22;35m"; +const char * ANSI_CYAN = "\033[22;36m"; +const char * ANSI_GREY = "\033[22;37m"; +const char * ANSI_DARKGREY = "\033[01;30m"; +const char * ANSI_LIGHTRED = "\033[01;31m"; +const char * ANSI_LIGHTGREEN = "\033[01;32m"; +const char * ANSI_YELLOW = "\033[01;33m"; +const char * ANSI_LIGHTBLUE = "\033[01;34m"; +const char * ANSI_LIGHTMAGENTA = "\033[01;35m"; +const char * ANSI_LIGHTCYAN = "\033[01;36m"; +const char * ANSI_WHITE = "\033[01;37m"; +const char * RESETCOLOR = "\033[0m"; + +const char * getANSIColor(const int c) +{ + switch (c) + { + case -1: return RESETCOLOR; // HACK! :P + case 0 : return ANSI_BLACK; + case 1 : return ANSI_BLUE; // non-ANSI + case 2 : return ANSI_GREEN; + case 3 : return ANSI_CYAN; // non-ANSI + case 4 : return ANSI_RED; // non-ANSI + case 5 : return ANSI_MAGENTA; + case 6 : return ANSI_BROWN; + case 7 : return ANSI_GREY; + case 8 : return ANSI_DARKGREY; + case 9 : return ANSI_LIGHTBLUE; // non-ANSI + case 10: return ANSI_LIGHTGREEN; + case 11: return ANSI_LIGHTCYAN; // non-ANSI; + case 12: return ANSI_LIGHTRED; // non-ANSI; + case 13: return ANSI_LIGHTMAGENTA; + case 14: return ANSI_YELLOW; // non-ANSI + case 15: return ANSI_WHITE; + default: return ""; + } +} + +namespace DFHack +{ + class Private + { + public: + Private() + { + dfout_C = NULL; + rawmode = false; + in_batch = false; + supported_terminal = false; + state = con_unclaimed; + }; + virtual ~Private() + { + //sync(); + } + private: + bool read_char(unsigned char & out) + { + FD_ZERO(&descriptor_set); + FD_SET(STDIN_FILENO, &descriptor_set); + FD_SET(exit_pipe[0], &descriptor_set); + int ret = TEMP_FAILURE_RETRY( + select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL) + ); + if(ret == -1) + return false; + if (FD_ISSET(exit_pipe[0], &descriptor_set)) + return false; + if (FD_ISSET(STDIN_FILENO, &descriptor_set)) + { + // read byte from stdin + ret = TEMP_FAILURE_RETRY( + read(STDIN_FILENO, &out, 1) + ); + if(ret == -1) + return false; + return true; + } + return false; + } + + public: + void print(const char *data) + { + fputs(data, dfout_C); + } + + void print_text(color_ostream::color_value clr, const std::string &chunk) + { + if(!in_batch && state == con_lineedit) + { + disable_raw(); + fprintf(dfout_C,"\x1b[1G"); + fprintf(dfout_C,"\x1b[0K"); + + color(clr); + print(chunk.c_str()); + + reset_color(); + enable_raw(); + prompt_refresh(); + } + else + { + color(clr); + print(chunk.c_str()); + } + } + + void begin_batch() + { + assert(!in_batch); + + in_batch = true; + + if (state == con_lineedit) + { + disable_raw(); + fprintf(dfout_C,"\x1b[1G"); + fprintf(dfout_C,"\x1b[0K"); + } + } + + void end_batch() + { + assert(in_batch); + + flush(); + + in_batch = false; + + if (state == con_lineedit) + { + reset_color(); + enable_raw(); + prompt_refresh(); + } + } + + void flush() + { + if (!rawmode) + fflush(dfout_C); + } + + /// Clear the console, along with its scrollback + void clear() + { + if(rawmode) + { + const char * clr = "\033c\033[3J\033[H"; + ::write(STDIN_FILENO,clr,strlen(clr)); + } + else + { + print("\033c\033[3J\033[H"); + fflush(dfout_C); + } + } + /// Position cursor at x,y. 1,1 = top left corner + void gotoxy(int x, int y) + { + char tmp[64]; + sprintf(tmp,"\033[%d;%dH", y,x); + print(tmp); + } + /// Set color (ANSI color number) + void color(Console::color_value index) + { + if(!rawmode) + fprintf(dfout_C,getANSIColor(index)); + else + { + const char * colstr = getANSIColor(index); + int lstr = strlen(colstr); + ::write(STDIN_FILENO,colstr,lstr); + } + } + /// Reset color to default + void reset_color(void) + { + color(Console::COLOR_RESET); + if(!rawmode) + fflush(dfout_C); + } + /// Enable or disable the caret/cursor + void cursor(bool enable = true) + { + if(enable) + print("\033[?25h"); + else + print("\033[?25l"); + } + /// Waits given number of milliseconds before continuing. + void msleep(unsigned int msec); + /// get the current number of columns + int get_columns(void) + { + winsize ws; + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80; + return ws.ws_col; + } + /// get the current number of rows + int get_rows(void) + { + winsize ws; + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25; + return ws.ws_row; + } + /// beep. maybe? + //void beep (void); + /// A simple line edit (raw mode) + int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch) + { + output.clear(); + reset_color(); + this->prompt = prompt; + if (!supported_terminal) + { + print(prompt.c_str()); + fflush(dfout_C); + // FIXME: what do we do here??? + //SDL_recursive_mutexV(lock); + std::getline(std::cin, output); + //SDL_recursive_mutexP(lock); + return output.size(); + } + else + { + int count; + if (enable_raw() == -1) return 0; + if(state == con_lineedit) + return -1; + state = con_lineedit; + count = prompt_loop(lock,ch); + state = con_unclaimed; + disable_raw(); + print("\n"); + if(count != -1) + { + output = raw_buffer; + } + return count; + } + } + int enable_raw() + { + struct termios raw; + + if (!supported_terminal) + return -1; + if (tcgetattr(STDIN_FILENO,&orig_termios) == -1) + return -1; + + raw = orig_termios; //modify the original mode + // input modes: no break, no CR to NL, no parity check, no strip char, + // no start/stop output control. + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + // output modes - disable post processing + raw.c_oflag &= ~(OPOST); + // control modes - set 8 bit chars + raw.c_cflag |= (CS8); + // local modes - choing off, canonical off, no extended functions, + // no signal chars (^Z,^C) +#ifdef CONSOLE_NO_CATCH + raw.c_lflag &= ~( ECHO | ICANON | IEXTEN ); +#else + raw.c_lflag &= ~( ECHO | ICANON | IEXTEN | ISIG ); +#endif + // control chars - set return condition: min number of bytes and timer. + // We want read to return every single byte, without timeout. + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer + // put terminal in raw mode after flushing + if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) < 0) + return -1; + rawmode = 1; + return 0; + } + + void disable_raw() + { + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig_termios) != -1) + rawmode = 0; + } + void prompt_refresh() + { + char seq[64]; + int cols = get_columns(); + int plen = prompt.size(); + const char * buf = raw_buffer.c_str(); + int len = raw_buffer.size(); + int cooked_cursor = raw_cursor; + // Use math! This is silly. + while((plen+cooked_cursor) >= cols) + { + buf++; + len--; + cooked_cursor--; + } + while (plen+len > cols) + { + len--; + } + /* Cursor to left edge */ + snprintf(seq,64,"\x1b[1G"); + if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; + /* Write the prompt and the current buffer content */ + if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return; + if (::write(STDIN_FILENO,buf,len) == -1) return; + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; + /* Move cursor to original position. */ + snprintf(seq,64,"\x1b[1G\x1b[%dC", (int)(cooked_cursor+plen)); + if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; + } + + int prompt_loop(recursive_mutex * lock, CommandHistory & history) + { + int fd = STDIN_FILENO; + size_t plen = prompt.size(); + int history_index = 0; + raw_buffer.clear(); + raw_cursor = 0; + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + const std::string empty; + history.add(empty); + if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1; + while(1) + { + unsigned char c; + int isok; + unsigned char seq[2], seq2; + lock->unlock(); + if(!read_char(c)) + { + lock->lock(); + return -2; + } + lock->lock(); + /* Only autocomplete when the callback is set. It returns < 0 when + * there was an error reading from fd. Otherwise it will return the + * character that should be handled next. */ + if (c == 9) + { + /* + if( completionCallback != NULL) { + c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols); + // Return on errors + if (c < 0) return len; + // Read next character when 0 + if (c == 0) continue; + } + else + { + // ignore tab + continue; + } + */ + // just ignore tabs + continue; + } + + switch(c) + { + case 13: // enter + history.remove(); + return raw_buffer.size(); + case 3: // ctrl-c + errno = EAGAIN; + return -1; + case 127: // backspace + case 8: // ctrl-h + if (raw_cursor > 0 && raw_buffer.size() > 0) + { + raw_buffer.erase(raw_cursor-1,1); + raw_cursor--; + prompt_refresh(); + } + break; + case 27: // escape sequence + lock->unlock(); + if(!read_char(seq[0]) || !read_char(seq[1])) + { + lock->lock(); + return -2; + } + lock->lock(); + if(seq[0] == '[') + { + if (seq[1] == 'D') + { + left_arrow: + if (raw_cursor > 0) + { + raw_cursor--; + prompt_refresh(); + } + } + else if ( seq[1] == 'C') + { + right_arrow: + /* right arrow */ + if (size_t(raw_cursor) != raw_buffer.size()) + { + raw_cursor++; + prompt_refresh(); + } + } + else if (seq[1] == 'A' || seq[1] == 'B') + { + /* up and down arrow: history */ + if (history.size() > 1) + { + /* Update the current history entry before to + * overwrite it with tne next one. */ + history[history_index] = raw_buffer; + /* Show the new entry */ + history_index += (seq[1] == 'A') ? 1 : -1; + if (history_index < 0) + { + history_index = 0; + break; + } + else if (size_t(history_index) >= history.size()) + { + history_index = history.size()-1; + break; + } + raw_buffer = history[history_index]; + raw_cursor = raw_buffer.size(); + prompt_refresh(); + } + } + else if(seq[1] == 'H') + { + // home + raw_cursor = 0; + prompt_refresh(); + } + else if(seq[1] == 'F') + { + // end + raw_cursor = raw_buffer.size(); + prompt_refresh(); + } + else if (seq[1] > '0' && seq[1] < '7') + { + // extended escape + lock->unlock(); + if(!read_char(seq2)) + { + lock->lock(); + return -2; + } + lock->lock(); + if (seq[1] == '3' && seq2 == '~' ) + { + // delete + if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size()) + { + raw_buffer.erase(raw_cursor,1); + prompt_refresh(); + } + } + } + } + break; + default: + if (raw_buffer.size() == size_t(raw_cursor)) + { + raw_buffer.append(1,c); + raw_cursor++; + if (plen+raw_buffer.size() < size_t(get_columns())) + { + /* Avoid a full update of the line in the + * trivial case. */ + if (::write(fd,&c,1) == -1) return -1; + } + else + { + prompt_refresh(); + } + } + else + { + raw_buffer.insert(raw_cursor,1,c); + raw_cursor++; + prompt_refresh(); + } + break; + case 21: // Ctrl+u, delete the whole line. + raw_buffer.clear(); + raw_cursor = 0; + prompt_refresh(); + break; + case 11: // Ctrl+k, delete from current to end of line. + raw_buffer.erase(raw_cursor); + prompt_refresh(); + break; + case 1: // Ctrl+a, go to the start of the line + raw_cursor = 0; + prompt_refresh(); + break; + case 5: // ctrl+e, go to the end of the line + raw_cursor = raw_buffer.size(); + prompt_refresh(); + break; + case 12: // ctrl+l, clear screen + clear(); + prompt_refresh(); + } + } + return raw_buffer.size(); + } + FILE * dfout_C; + bool supported_terminal; + // state variables + bool rawmode; // is raw mode active? + termios orig_termios; // saved/restored by raw mode + // current state + enum console_state + { + con_unclaimed, + con_lineedit + } state; + bool in_batch; + std::string prompt; // current prompt string + std::string raw_buffer; // current raw mode buffer + int raw_cursor; // cursor position in the buffer + // thread exit mechanism + int exit_pipe[2]; + fd_set descriptor_set; + }; +} + +Console::Console() +{ + d = 0; + inited = false; + // we can't create the mutex at this time. the SDL functions aren't hooked yet. + wlock = new recursive_mutex(); +} +Console::~Console() +{ + if(inited) + shutdown(); + if(wlock) + delete wlock; + if(d) + delete d; +} + +bool Console::init(bool sharing) +{ + if(sharing) + { + inited = false; + return false; + } + freopen("stdout.log", "w", stdout); + d = new Private(); + // make our own weird streams so our IO isn't redirected + d->dfout_C = fopen("/dev/tty", "w"); + std::cin.tie(this); + clear(); + d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO); + // init the exit mechanism + pipe(d->exit_pipe); + FD_ZERO(&d->descriptor_set); + FD_SET(STDIN_FILENO, &d->descriptor_set); + FD_SET(d->exit_pipe[0], &d->descriptor_set); + inited = true; + return true; +} + +bool Console::shutdown(void) +{ + if(!d) + return true; + lock_guard g(*wlock); + if(d->rawmode) + d->disable_raw(); + d->print("\n"); + inited = false; + // kill the thing + close(d->exit_pipe[1]); + return true; +} + +void Console::begin_batch() +{ + //color_ostream::begin_batch(); + + wlock->lock(); + + if (inited) + d->begin_batch(); +} + +void Console::end_batch() +{ + if (inited) + d->end_batch(); + + wlock->unlock(); +} + +void Console::flush_proxy() +{ + lock_guard g(*wlock); + if (inited) + d->flush(); +} + +void Console::add_text(color_value color, const std::string &text) +{ + lock_guard g(*wlock); + if (inited) + d->print_text(color, text); +} + +int Console::get_columns(void) +{ + lock_guard g(*wlock); + int ret = -1; + if(inited) + ret = d->get_columns(); + return ret; +} + +int Console::get_rows(void) +{ + lock_guard g(*wlock); + int ret = -1; + if(inited) + ret = d->get_rows(); + return ret; +} + +void Console::clear() +{ + lock_guard g(*wlock); + if(inited) + d->clear(); +} + +void Console::gotoxy(int x, int y) +{ + lock_guard g(*wlock); + if(inited) + d->gotoxy(x,y); +} + +void Console::cursor(bool enable) +{ + lock_guard g(*wlock); + if(inited) + d->cursor(enable); +} + +int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch) +{ + lock_guard g(*wlock); + int ret = -2; + if(inited) + ret = d->lineedit(prompt,output,wlock,ch); + return ret; +} + +void Console::msleep (unsigned int msec) +{ + if (msec > 1000) sleep(msec/1000000); + usleep((msec % 1000000) * 1000); +} \ No newline at end of file diff --git a/library/Hooks-darwin.cpp b/library/Hooks-darwin.cpp new file mode 100644 index 000000000..59539c79b --- /dev/null +++ b/library/Hooks-darwin.cpp @@ -0,0 +1,169 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*typedef struct interpose_s +{ + void *new_func; + void *orig_func; +} interpose_t;*/ + +#include "DFHack.h" +#include "Core.h" +#include "Hooks.h" +#include + +#include "MacPool.h" + +/*static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) = +{ + { (void *)DFH_SDL_Init, (void *)SDL_Init }, + { (void *)DFH_SDL_PollEvent, (void *)SDL_PollEvent }, + { (void *)DFH_SDL_Quit, (void *)SDL_Quit }, + { (void *)DFH_SDL_NumJoysticks, (void *)SDL_NumJoysticks }, + +};*/ + +/******************************************************************************* +* SDL part starts here * +*******************************************************************************/ +// hook - called for each game tick (or more often) +DFhackCExport int SDL_NumJoysticks(void) +{ + DFHack::Core & c = DFHack::Core::getInstance(); + return c.Update(); +} + +// hook - called at program exit +static void (*_SDL_Quit)(void) = 0; +DFhackCExport void SDL_Quit(void) +{ + DFHack::Core & c = DFHack::Core::getInstance(); + c.Shutdown(); + /*if(_SDL_Quit) + { + _SDL_Quit(); + }*/ + +// destroy_pool(); + + _SDL_Quit(); +} + +// called by DF to check input events +static int (*_SDL_PollEvent)(SDL_Event* event) = 0; +DFhackCExport int SDL_PollEvent(SDL_Event* event) +{ + pollevent_again: + // if SDL returns 0 here, it means there are no more events. return 0 + int orig_return = _SDL_PollEvent(event); + if(!orig_return) + return 0; + // otherwise we have an event to filter + else if( event != 0 ) + { + DFHack::Core & c = DFHack::Core::getInstance(); + // if we consume the event, ask SDL for more. + if(!c.DFH_SDL_Event(event)) + goto pollevent_again; + } + return orig_return; +} + +struct WINDOW; +DFhackCExport int wgetch(WINDOW *win) +{ + static int (*_wgetch)(WINDOW * win) = (int (*)( WINDOW * )) dlsym(RTLD_NEXT, "wgetch"); + if(!_wgetch) + { + exit(EXIT_FAILURE); + } + DFHack::Core & c = DFHack::Core::getInstance(); + wgetch_again: + int in = _wgetch(win); + int out; + if(c.ncurses_wgetch(in, out)) + { + // not consumed, give to DF + return out; + } + else + { + // consumed, repeat + goto wgetch_again; + } +} + +// hook - called at program start, initialize some stuffs we'll use later +static int (*_SDL_Init)(uint32_t flags) = 0; +DFhackCExport int SDL_Init(uint32_t flags) +{ + // reroute stderr + fprintf(stderr,"dfhack: attempting to hook in\n"); + //freopen("stderr.log", "w", stderr); + // we don't reroute stdout until we figure out if this should be done at all + // See: Console-linux.cpp + +// create_pool(); + + // find real functions + fprintf(stderr,"dfhack: saving real SDL functions\n"); + _SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init"); + _SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit"); + _SDL_PollEvent = (int (*)(SDL_Event*))dlsym(RTLD_NEXT,"SDL_PollEvent"); + + fprintf(stderr,"dfhack: saved real SDL functions\n"); + // check if we got them + if(_SDL_Init && _SDL_Quit && _SDL_PollEvent) + { + fprintf(stderr,"dfhack: hooking successful\n"); + } + else + { + // bail, this would be a disaster otherwise + fprintf(stderr,"dfhack: something went horribly wrong\n"); + exit(1); + } + + DFHack::Core & c = DFHack::Core::getInstance(); + c.Init(); + + int ret = _SDL_Init(flags); + return ret; +} \ No newline at end of file diff --git a/library/PlugLoad-darwin.cpp b/library/PlugLoad-darwin.cpp new file mode 100644 index 000000000..69945c6f4 --- /dev/null +++ b/library/PlugLoad-darwin.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DFHack.h" +#include "PluginManager.h" +#include "Hooks.h" +#include + +/* + * Plugin loading functions + */ +namespace DFHack +{ + DFLibrary * OpenPlugin (const char * filename) + { + dlerror(); + DFLibrary * ret = (DFLibrary *) dlopen(filename, RTLD_NOW); + if(!ret) + { + std::cerr << dlerror() << std::endl; + } + return ret; + } + void * LookupPlugin (DFLibrary * plugin ,const char * function) + { + return (DFLibrary *) dlsym((void *)plugin, function); + } + void ClosePlugin (DFLibrary * plugin) + { + dlclose((void *) plugin); + } +} \ No newline at end of file diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp new file mode 100644 index 000000000..a4b95186a --- /dev/null +++ b/library/Process-darwin.cpp @@ -0,0 +1,229 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include "MemAccess.h" +#include "VersionInfoFactory.h" +#include "VersionInfo.h" +#include "Error.h" +#include +using namespace DFHack; + +Process::Process(VersionInfoFactory * known_versions) +{ + int target_result; + + char path[1024]; + char *real_path; + uint32_t size = sizeof(path); + if (_NSGetExecutablePath(path, &size) == 0) { + real_path = realpath(path, NULL); + } + + identified = false; + my_descriptor = 0; + + md5wrapper md5; + uint32_t length; + uint8_t first_kb [1024]; + memset(first_kb, 0, sizeof(first_kb)); + // get hash of the running DF process + string hash = md5.getHashFromFile(real_path, length, (char *) first_kb); + // create linux process, add it to the vector + VersionInfo * vinfo = known_versions->getVersionInfoByMD5(hash); + if(vinfo) + { + my_descriptor = new VersionInfo(*vinfo); + identified = true; + } + else + { + char * wd = getcwd(NULL, 0); + cerr << "Unable to retrieve version information.\n"; + cerr << "File: " << real_path << endl; + cerr << "MD5: " << hash << endl; + cerr << "working dir: " << wd << endl; + cerr << "length:" << length << endl; + cerr << "1KB hexdump follows:" << endl; + for(int i = 0; i < 64; i++) + { + fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + first_kb[i*16], + first_kb[i*16+1], + first_kb[i*16+2], + first_kb[i*16+3], + first_kb[i*16+4], + first_kb[i*16+5], + first_kb[i*16+6], + first_kb[i*16+7], + first_kb[i*16+8], + first_kb[i*16+9], + first_kb[i*16+10], + first_kb[i*16+11], + first_kb[i*16+12], + first_kb[i*16+13], + first_kb[i*16+14], + first_kb[i*16+15] + ); + } + free(wd); + } +} + +Process::~Process() +{ + // destroy our copy of the memory descriptor + delete my_descriptor; +} + +string Process::doReadClassName (void * vptr) +{ + //FIXME: BAD!!!!! + char * typeinfo = Process::readPtr(((char *)vptr - 0x4)); + char * typestring = Process::readPtr(typeinfo + 0x4); + string raw = readCString(typestring); + size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers + size_t end = raw.length(); + return raw.substr(start,end-start); +} + +//FIXME: cross-reference with ELF segment entries? +void Process::getMemRanges( vector & ranges ) +{ + char buffer[1024]; + char permissions[5]; // r/-, w/-, x/-, p/s, 0 + + FILE *mapFile = ::fopen("/proc/self/maps", "r"); + size_t start, end, offset, device1, device2, node; + + while (fgets(buffer, 1024, mapFile)) + { + t_memrange temp; + temp.name[0] = 0; + sscanf(buffer, "%zx-%zx %s %zx %2zx:%2zx %zu %[^\n]", + &start, + &end, + (char*)&permissions, + &offset, &device1, &device2, &node, + (char*)temp.name); + temp.start = (void *) start; + temp.end = (void *) end; + temp.read = permissions[0] == 'r'; + temp.write = permissions[1] == 'w'; + temp.execute = permissions[2] == 'x'; + temp.shared = permissions[3] == 's'; + temp.valid = true; + ranges.push_back(temp); + } +} + +uint32_t Process::getBase() +{ + return 0; +} + +static int getdir (string dir, vector &files) +{ + DIR *dp; + struct dirent *dirp; + if((dp = opendir(dir.c_str())) == NULL) + { + cout << "Error(" << errno << ") opening " << dir << endl; + return errno; + } + while ((dirp = readdir(dp)) != NULL) { + files.push_back(string(dirp->d_name)); + } + closedir(dp); + return 0; +} + +bool Process::getThreadIDs(vector & threads ) +{ + stringstream ss; + vector subdirs; + if(getdir("/proc/self/task/",subdirs) != 0) + { + //FIXME: needs exceptions. this is a fatal error + cerr << "unable to enumerate threads. This is BAD!" << endl; + return false; + } + threads.clear(); + for(size_t i = 0; i < subdirs.size();i++) + { + uint32_t tid; + if(sscanf(subdirs[i].c_str(),"%d", &tid)) + { + threads.push_back(tid); + } + } + return true; +} + +string Process::getPath() +{ + char path[1024]; + char *real_path; + uint32_t size = sizeof(path); + if (_NSGetExecutablePath(path, &size) == 0) { + real_path = realpath(path, NULL); + } + std::string path_string(real_path); + int last_slash = path_string.find_last_of("/"); + std::string directory = path_string.substr(0,last_slash); + return directory; +} + +int Process::getPID() +{ + return getpid(); +} + +bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange) +{ + int result; + int protect=0; + if(trgrange.read)protect|=PROT_READ; + if(trgrange.write)protect|=PROT_WRITE; + if(trgrange.execute)protect|=PROT_EXEC; + result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect); + + return result==0; +} \ No newline at end of file diff --git a/library/include/Core.h b/library/include/Core.h index 2b7b69d4b..2810ff14a 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -84,10 +84,10 @@ namespace DFHack // Better than tracking some weird variables all over the place. class DFHACK_EXPORT Core { - friend int ::DFH_SDL_NumJoysticks(void); - friend void ::DFH_SDL_Quit(void); - friend int ::DFH_SDL_PollEvent(SDL_Event *); - friend int ::DFH_SDL_Init(uint32_t flags); + friend int ::SDL_NumJoysticks(void); + friend void ::SDL_Quit(void); + friend int ::SDL_PollEvent(SDL_Event *); + friend int ::SDL_Init(uint32_t flags); friend int ::wgetch(WINDOW * w); friend int ::egg_init(void); friend int ::egg_shutdown(void); diff --git a/library/include/Hooks.h b/library/include/Hooks.h index 7d7f96504..83a39ea18 100644 --- a/library/include/Hooks.h +++ b/library/include/Hooks.h @@ -46,10 +46,10 @@ namespace SDL // these functions are here because they call into DFHack::Core and therefore need to // be declared as friend functions/known -DFhackCExport int DFH_SDL_NumJoysticks(void); -DFhackCExport void DFH_SDL_Quit(void); -DFhackCExport int DFH_SDL_PollEvent(SDL_Event* event); -DFhackCExport int DFH_SDL_Init(uint32_t flags); +DFhackCExport int SDL_NumJoysticks(void); +DFhackCExport void SDL_Quit(void); +DFhackCExport int SDL_PollEvent(SDL_Event* event); +DFhackCExport int SDL_Init(uint32_t flags); DFhackCExport int wgetch(WINDOW * win); // hook - called early from DF's main()