/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include "libxl.h" #include "libxl_utils.h" #include "libxl_internal.h" #include "flexarray.h" int libxl_ctx_init(struct libxl_ctx *ctx) { memset(ctx, 0, sizeof(struct libxl_ctx)); ctx->alloc_maxsize = 256; ctx->alloc_ptrs = calloc(ctx->alloc_maxsize, sizeof(void *)); if (!ctx->alloc_ptrs) return ERROR_NOMEM; ctx->xch = xc_interface_open(); ctx->xsh = xs_daemon_open(); return 0; } int libxl_ctx_free(struct libxl_ctx *ctx) { libxl_free_all(ctx); free(ctx->alloc_ptrs); ctx->alloc_ptrs = NULL; xc_interface_close(ctx->xch); xs_daemon_close(ctx->xsh); return 0; } int libxl_ctx_set_log(struct libxl_ctx *ctx, libxl_log_callback log_callback, void *log_data) { ctx->log_callback = log_callback; ctx->log_userdata = log_data; return 0; } /******************************************************************************/ int libxl_domain_make(struct libxl_ctx *ctx, libxl_domain_create_info *info, uint32_t *domid) { int flags, ret, i; char *uuid_string; char *rw_paths[] = { "device" }; char *ro_paths[] = { "cpu", "memory", "device", "error", "drivers", "control", "attr", "data", "messages" }; char *dom_path, *vm_path, *vss_path; struct xs_permissions roperm[2]; struct xs_permissions rwperm[1]; xs_transaction_t t; uuid_string = uuid_to_string(ctx, info->uuid); if (!uuid_string) { XL_LOG(ctx, XL_LOG_ERROR, "missing uuid"); return ERROR_FAIL; } flags = info->hvm ? XEN_DOMCTL_CDF_hvm_guest : 0; flags |= info->hap ? XEN_DOMCTL_CDF_hap : 0; *domid = 0; ret = xc_domain_create(ctx->xch, info->ssidref, info->uuid, flags, domid); if (ret < 0) { XL_LOG(ctx, XL_LOG_ERROR, "domain creation fail: %d", ret); return ERROR_FAIL; } dom_path = libxl_xs_get_dompath(ctx, *domid); vm_path = libxl_sprintf(ctx, "/vm/%s", uuid_string); vss_path = libxl_sprintf(ctx, "/vss/%s", uuid_string); if (!dom_path || !vm_path || !vss_path) { XL_LOG(ctx, XL_LOG_ERROR, "cannot allocate create paths"); return ERROR_FAIL; } roperm[0].id = 0; roperm[0].perms = XS_PERM_NONE; roperm[1].id = *domid; roperm[1].perms = XS_PERM_READ; rwperm[0].id = *domid; rwperm[0].perms = XS_PERM_NONE; retry_transaction: t = xs_transaction_start(ctx->xsh); xs_rm(ctx->xsh, t, dom_path); xs_mkdir(ctx->xsh, t, dom_path); xs_set_permissions(ctx->xsh, t, dom_path, roperm, ARRAY_SIZE(roperm)); xs_rm(ctx->xsh, t, vm_path); xs_mkdir(ctx->xsh, t, vm_path); xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm)); xs_rm(ctx->xsh, t, vss_path); xs_mkdir(ctx->xsh, t, vss_path); xs_set_permissions(ctx->xsh, t, vss_path, rwperm, ARRAY_SIZE(rwperm)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vm", dom_path), vm_path, strlen(vm_path)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vss", dom_path), vss_path, strlen(vss_path)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/name", dom_path), info->name, strlen(info->name)); for (i = 0; i < ARRAY_SIZE(rw_paths); i++) { char *path = libxl_sprintf(ctx, "%s/%s", dom_path, rw_paths[i]); xs_mkdir(ctx->xsh, t, path); xs_set_permissions(ctx->xsh, t, path, rwperm, ARRAY_SIZE(rwperm)); libxl_free(ctx, path); } for (i = 0; i < ARRAY_SIZE(ro_paths); i++) { char *path = libxl_sprintf(ctx, "%s/%s", dom_path, ro_paths[i]); xs_mkdir(ctx->xsh, t, path); xs_set_permissions(ctx->xsh, t, path, roperm, ARRAY_SIZE(roperm)); libxl_free(ctx, path); } xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/uuid", vm_path), uuid_string, strlen(uuid_string)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/name", vm_path), info->name, strlen(info->name)); libxl_xs_writev(ctx, t, dom_path, info->xsdata); libxl_xs_writev(ctx, t, libxl_sprintf(ctx, "%s/platform", dom_path), info->platformdata); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; return 0; } int libxl_domain_build(struct libxl_ctx *ctx, libxl_domain_build_info *info, uint32_t domid) { libxl_domain_build_state state; char **vments = NULL, **localents = NULL; memset(&state, '\0', sizeof(state)); build_pre(ctx, domid, info, &state); if (info->hvm) { build_hvm(ctx, domid, info, &state); vments = libxl_calloc(ctx, 4, sizeof(char *)); vments[0] = libxl_sprintf(ctx, "rtc/timeoffset"); vments[1] = libxl_sprintf(ctx, "%s", (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""); } else { build_pv(ctx, domid, info, &state); } build_post(ctx, domid, info, &state, vments, localents); return 0; } int libxl_domain_restore(struct libxl_ctx *ctx, libxl_domain_build_info *info, uint32_t domid, int fd) { libxl_domain_build_state state; char **vments = NULL, **localents = NULL; memset(&state, '\0', sizeof(state)); build_pre(ctx, domid, info, &state); restore_common(ctx, domid, info, &state, fd); if (info->hvm) { vments = libxl_calloc(ctx, 4, sizeof(char *)); vments[0] = libxl_sprintf(ctx, "rtc/timeoffset"); vments[1] = libxl_sprintf(ctx, "%s", (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""); } else { localents = libxl_calloc(ctx, 4 * 2, sizeof(char *)); localents[0] = libxl_sprintf(ctx, "serial/0/limit"); localents[1] = libxl_sprintf(ctx, "%d", 65536); localents[2] = libxl_sprintf(ctx, "console/port"); localents[3] = libxl_sprintf(ctx, "%d", state.console_port); localents[4] = libxl_sprintf(ctx, "console/ring-ref"); localents[5] = libxl_sprintf(ctx, "%ld", state.console_mfn); } build_post(ctx, domid, info, &state, vments, localents); return 0; } struct libxl_dominfo * libxl_domain_list(struct libxl_ctx *ctx, int *nb_domain) { struct libxl_dominfo *ptr; int index, i, ret, first_domain; xc_domaininfo_t info[16]; int size = 16; first_domain = 1; index = 0; ptr = libxl_calloc(ctx, size, sizeof(struct libxl_dominfo)); if (!ptr) return NULL; redo: ret = xc_domain_getinfolist(ctx->xch, first_domain, 16, info); for (i = 0; i < ret; i++) { if (index == size) { struct libxl_dominfo *ptr2; ptr2 = libxl_calloc(ctx, size * 2, sizeof(struct libxl_dominfo)); if (!ptr2) { libxl_free(ctx, ptr); return NULL; } memcpy(ptr2, ptr, sizeof(struct libxl_dominfo) * size); libxl_free(ctx, ptr); ptr = ptr2; size *= 2; } memcpy(ptr[index].uuid, info[i].handle, 16 * sizeof(uint8_t)); ptr[index].domid = info[i].domain; first_domain = info[i].domain + 1; index++; } if (ret == 16) goto redo; *nb_domain = index; return ptr; } xc_dominfo_t * libxl_domain_infolist(struct libxl_ctx *ctx, int *nb_domain) { int index, first_domain; xc_dominfo_t *info; int size = 1024; first_domain = 0; index = 0; info = (xc_dominfo_t *) libxl_calloc(ctx, size, sizeof(xc_dominfo_t)); if (!info) { *nb_domain = 0; return NULL; } *nb_domain = xc_domain_getinfo(ctx->xch, first_domain, 1024, info); return info; } int libxl_domain_suspend(struct libxl_ctx *ctx, libxl_domain_suspend_info *info, uint32_t domid, int fd) { int hvm = 1; int live = 0; int debug = 0; char savesig[] = "XenSavedDomain\n"; write(fd, savesig, strlen(savesig)); core_suspend(ctx, domid, fd, hvm, live, debug); return 0; } int libxl_domain_pause(struct libxl_ctx *ctx, uint32_t domid) { xc_domain_pause(ctx->xch, domid); return 0; } int libxl_domain_unpause(struct libxl_ctx *ctx, uint32_t domid) { xc_domain_unpause(ctx->xch, domid); return 0; } static char *req_table[] = { [0] = "poweroff", [1] = "reboot", [2] = "suspend", [3] = "crash", [4] = "halt", }; int libxl_domain_shutdown(struct libxl_ctx *ctx, uint32_t domid, int req) { char *shutdown_path; char *dom_path; if (req > ARRAY_SIZE(req_table)) return ERROR_INVAL; dom_path = libxl_xs_get_dompath(ctx, domid); shutdown_path = libxl_sprintf(ctx, "%s/control/shutdown", dom_path); xs_write(ctx->xsh, XBT_NULL, shutdown_path, req_table[req], strlen(req_table[req])); if (/* hvm */ 0) { unsigned long acpi_s_state = 0; unsigned long pvdriver = 0; xc_get_hvm_param(ctx->xch, domid, HVM_PARAM_ACPI_S_STATE, &acpi_s_state); xc_get_hvm_param(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver); if (!pvdriver && acpi_s_state != 0) xc_domain_shutdown(ctx->xch, domid, req); } return 0; } static int libxl_destroy_device_model(struct libxl_ctx *ctx, uint32_t domid) { char *pid; int ret; pid = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/image/device-model-pid", domid)); if (!pid) { XL_LOG(ctx, XL_LOG_ERROR, "Couldn't find device model's pid\n"); return -1; } xs_rm(ctx->xsh, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d", domid)); ret = kill(atoi(pid), SIGHUP); if (ret < 0 && errno == ESRCH) { XL_LOG(ctx, XL_LOG_DEBUG, "Device Model already exited\n"); ret = 0; } else if (ret == 0) { XL_LOG(ctx, XL_LOG_DEBUG, "Device Model signaled\n"); ret = 0; } else { XL_LOG(ctx, XL_LOG_ERROR, "kill %d returned %d errno=%d\n", atoi(pid), ret, errno); } return ret; } int libxl_domain_destroy(struct libxl_ctx *ctx, uint32_t domid, int force) { char *dom_path, vm_path[41]; uint8_t *uuid; dom_path = libxl_xs_get_dompath(ctx, domid); if (!dom_path) { XL_LOG(ctx, XL_LOG_ERROR, "dompath doesn't exist for %d\n", domid); return -1; } if (libxl_domid_to_uuid(ctx, &uuid, domid) < 0) { XL_LOG(ctx, XL_LOG_ERROR, "failed ot get uuid for %d\n", domid); return -1; } xs_write(ctx->xsh, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid), "shutdown", strlen("shutdown")); if (xc_domain_pause(ctx->xch, domid) < 0) { XL_LOG(ctx, XL_LOG_ERROR, "xc_domain_pause failed for %d\n", domid); return -1; } /* do_FLR */ if (xc_domain_destroy(ctx->xch, domid) < 0) { XL_LOG(ctx, XL_LOG_ERROR, "xc_domain_destroy failed for %d\n", domid); return -1; } if (libxl_devices_destroy(ctx, domid, force) < 0) XL_LOG(ctx, XL_LOG_ERROR, "libxl_destroy_devices failed for %d\n", domid); if (libxl_destroy_device_model(ctx, domid) < 0) XL_LOG(ctx, XL_LOG_ERROR, "libxl_destroy_device_model failed for %d\n", domid); if (!xs_rm(ctx->xsh, XBT_NULL, dom_path)) XL_LOG(ctx, XL_LOG_ERROR, "xs_rm failed for %s\n", dom_path); snprintf(vm_path, sizeof(vm_path), "/vm/%s", uuid_to_string(ctx, uuid)); if (!xs_rm(ctx->xsh, XBT_NULL, vm_path)) XL_LOG(ctx, XL_LOG_ERROR, "xs_rm failed for %s\n", vm_path); return 0; } static char ** libxl_build_device_model_args(struct libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_nic *vifs, int num_vifs) { int num = 0, i; flexarray_t *dm_args; dm_args = flexarray_make(16, 1); if (!dm_args) return NULL; flexarray_set(dm_args, num++, libxl_sprintf(ctx, "qemu-dm")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-d")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%d", info->domid)); if (info->dom_name) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-domain-name")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->dom_name)); } if (info->videoram) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-videoram")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%d", info->videoram)); } if (info->stdvga) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-std-vga")); } if (info->vnc || info->vncdisplay || info->vnclisten || info->vncunused) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-vnc")); if (info->vncdisplay) { if (info->vnclisten && strchr(info->vnclisten, ':') == NULL) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s:%d", info->vnclisten, info->vncdisplay)); } else { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "127.0.0.1:%d", info->vncdisplay)); } } else if (info->vnclisten) { if (strchr(info->vnclisten, ':') != NULL) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->vnclisten)); } else { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s:0", info->vnclisten)); } } else { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "127.0.0.1:0")); } if (info->vncunused) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-vncunused")); } } if (info->sdl || info->opengl) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-sdl")); if (info->opengl) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-disable-opengl")); } } if (info->keymap) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-k")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->keymap)); } if (info->nographic && (!info->sdl && !info->vnc)) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-nographic")); } if (info->serial) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-serial")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->serial)); } if (info->boot) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-boot")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->boot)); } if (info->usb) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-usb")); if (info->usbdevice) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-usbdevice")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->usbdevice)); } } if (info->apic) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-acpi")); } if (info->extra) { int i = 0; while (info->extra[i] != NULL) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->extra[i])); } } for (i = 0; i < num_vifs; i++) { if (vifs[i].nictype == NICTYPE_IOEMU) { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-net")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "nic,vlan=%d,macaddr=%s,model=%s", vifs[i].devid, vifs[i].smac, vifs[i].model)); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-net")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "tap,vlan=%d,ifname=%s,bridge=%s", vifs[i].devid, vifs[i].ifname, vifs[i].bridge)); } } flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-M")); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "xenfv")); flexarray_set(dm_args, num++, NULL); return (char **) flexarray_contents(dm_args); } int libxl_create_device_model(struct libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_nic *vifs, int num_vifs) { char *dom_path, *path, *logfile, *logfile_new; char *kvs[3]; struct stat stat_buf; int logfile_w, null, pid; int i; char **args; args = libxl_build_device_model_args(ctx, info, vifs, num_vifs); if (!args) return ERROR_FAIL; dom_path = libxl_xs_get_dompath(ctx, info->domid); path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d", info->domid); xs_mkdir(ctx->xsh, XBT_NULL, path); logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log", info->dom_name); if (stat(logfile, &stat_buf) == 0) { /* file exists, rotate */ logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.10", info->dom_name); unlink(logfile); for (i = 9; i > 0; i--) { logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.%d", info->dom_name, i); logfile_new = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.%d", info->dom_name, i + 1); rename(logfile, logfile_new); } logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log", info->dom_name); logfile_new = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.1", info->dom_name); rename(logfile, logfile_new); } logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log", info->dom_name); logfile_w = open(logfile, O_WRONLY|O_CREAT, 0644); null = open("/dev/null", O_RDONLY); pid = libxl_exec(ctx, null, logfile_w, logfile_w, info->device_model, args); close(null); close(logfile_w); kvs[0] = libxl_sprintf(ctx, "image/device-model-pid"); kvs[1] = libxl_sprintf(ctx, "%d", pid); kvs[2] = NULL; libxl_xs_writev(ctx, XBT_NULL, dom_path, kvs); return 0; } /******************************************************************************/ int libxl_device_disk_add(struct libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk) { flexarray_t *front; flexarray_t *back; char *backend_type; unsigned int boffset = 0; unsigned int foffset = 0; int devid; libxl_device device; front = flexarray_make(16, 1); if (!front) return ERROR_NOMEM; back = flexarray_make(16, 1); if (!back) /* leaks front if error */ return ERROR_NOMEM; backend_type = device_disk_backend_type_of_phystype(disk->phystype); devid = device_disk_dev_number(disk->virtpath); device.backend_devid = devid; device.backend_domid = disk->backend_domid; device.devid = devid; device.domid = disk->domid; device.kind = DEVICE_VBD; switch (disk->phystype) { case PHYSTYPE_FILE: return ERROR_NI; /* FIXME */ break; case PHYSTYPE_PHY: { int major, minor; device_disk_major_minor(disk->virtpath, &major, &minor); flexarray_set(back, boffset++, libxl_sprintf(ctx, "physical-device")); flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x:%x", major, minor)); flexarray_set(back, boffset++, libxl_sprintf(ctx, "params")); flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", disk->physpath)); device.backend_kind = DEVICE_VBD; break; } case PHYSTYPE_AIO: case PHYSTYPE_QCOW: case PHYSTYPE_QCOW2: case PHYSTYPE_VHD: flexarray_set(back, boffset++, libxl_sprintf(ctx, "params")); flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s:%s", device_disk_string_of_phystype(disk->phystype), disk->physpath)); device.backend_kind = DEVICE_TAP; break; } flexarray_set(back, boffset++, libxl_sprintf(ctx, "frontend-id")); flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", disk->domid)); flexarray_set(back, boffset++, libxl_sprintf(ctx, "online")); flexarray_set(back, boffset++, libxl_sprintf(ctx, "1")); flexarray_set(back, boffset++, libxl_sprintf(ctx, "removable"))
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
//
// $Log:$
//
// DESCRIPTION:
//	Main loop menu stuff.
//	Default Config File.
//	PCX Screenshots.
//
//-----------------------------------------------------------------------------

static const char
rcsid[] = "$Id: m_misc.c,v 1.6 1997/02/03 22:45:10 b1 Exp $";

#include <ctype.h>


#include "doomdef.h"

#include "z_zone.h"

#include "m_swap.h"
#include "m_argv.h"

#include "w_wad.h"

#include "i_system.h"
#include "i_video.h"
#include "v_video.h"

#include "hu_stuff.h"

// State.
#include "doomstat.h"

// Data.
#include "dstrings.h"

#include "m_misc.h"

//
// M_DrawText
// Returns the final X coordinate
// HU_Init must have been called to init the font
//
extern patch_t*		hu_font[HU_FONTSIZE];

int
M_DrawText
( int		x,
  int		y,
  boolean	direct,
  char*		string )
{
    int 	c;
    int		w;

    while (*string)
    {
	c = toupper(*string) - HU_FONTSTART;
	string++;
	if (c < 0 || c> HU_FONTSIZE)
	{
	    x += 4;
	    continue;
	}
		
	w = SHORT (hu_font[c]->width);
	if (x+w > SCREENWIDTH)
	    break;
	if (direct)
	    V_DrawPatchDirect(x, y, 0, hu_font[c]);
	else
	    V_DrawPatch(x, y, 0, hu_font[c]);
	x+=w;
    }

    return x;
}




//
// M_WriteFile
//
#ifndef O_BINARY
#define O_BINARY 0
#endif

boolean
M_WriteFile
( char const*	name,
  void*		source,
  int		length )
{
    int		handle;
    int		count;
	
    handle = I_FileCreate(name);

    if (handle == -1)
	return false;

    count = I_FileWrite (handle, source, length);
    I_FileClose(handle);
	
    if (count < length)
	return false;
		
    return true;
}


//
// M_ReadFile
//
int
M_ReadFile
( char const*	name,
  byte**	buffer )
{
    int	handle, count, length;
    byte		*buf;
	
    handle = I_FileOpenRead (name);
    if (handle == -1)
	I_Error ("Couldn't read file %s", name);
    length = I_FileSize(handle);
    buf = Z_Malloc (length, PU_STATIC, NULL);
    count = I_FileRead (handle, buf, length);
    I_FileClose (handle);
	
    if (count < length)
	I_Error ("Couldn't read file %s", name);
		
    *buffer = buf;
    return length;
}


//
// DEFAULTS
//
int		usemouse;
int		usejoystick;

extern int	key_right;
extern int	key_left;
extern int	key_up;
extern int	key_down;

extern int	key_strafeleft;
extern int	key_straferight;

extern int	key_fire;
extern int	key_use;
extern int	key_strafe;
extern int	key_speed;

extern int	mousebfire;
extern int	mousebstrafe;
extern int	mousebforward;

extern int	joybfire;
extern int	joybstrafe;
extern int	joybuse;
extern int	joybspeed;

extern int	viewwidth;
extern int	viewheight;

extern int	mouseSensitivity;
extern int	showMessages;

extern int	detailLevel;

extern int	screenblocks;

extern int	showMessages;

// machine-independent sound params
extern	int	numChannels;


// UNIX hack, to be removed.
#ifdef SNDSERV
extern char*	sndserver_filename;
extern int	mb_used;
#endif

#ifdef LINUX
char*		mousetype;
char*		mousedev;
#endif

extern char*	chat_macros[];



typedef struct
{
    char*	name;
    int*	location;
    int		defaultvalue;
    int		scantranslate;		// PC scan code hack
    int		untranslated;		// lousy hack
} default_t;

default_t	defaults[] =
{
    {"mouse_sensitivity",&mouseSensitivity, 5},
    {"sfx_volume",&snd_SfxVolume, 8},
    {"music_volume",&snd_MusicVolume, 8},
    {"show_messages",&showMessages, 1},
    

#ifdef NORMALUNIX
    {"key_right",&key_right, KEY_RIGHTARROW},
    {"key_left",&key_left, KEY_LEFTARROW},
    {"key_up",&key_up, KEY_UPARROW},
    {"key_down",&key_down, KEY_DOWNARROW},
    {"key_strafeleft",&key_strafeleft, ','},
    {"key_straferight",&key_straferight, '.'},

    {"key_fire",&key_fire, KEY_RCTRL},
    {"key_use",&key_use, ' '},
    {"key_strafe",&key_strafe, KEY_RALT},
    {"key_speed",&key_speed, KEY_RSHIFT},

// UNIX hack, to be removed. 
#ifdef SNDSERV
    {"sndserver", (int *) &sndserver_filename, (int) "sndserver"},
    {"mb_used", &mb_used, 2},
#endif
    
#endif

#ifdef LINUX
    {"mousedev", (int*)&mousedev, (int)"/dev/ttyS0"},
    {"mousetype", (int*)&mousetype, (int)"microsoft"},
#endif

    {"use_mouse",&usemouse, 1},
    {"mouseb_fire",&mousebfire,0},
    {"mouseb_strafe",&mousebstrafe,1},
    {"mouseb_forward",&mousebforward,2},

    {"use_joystick",&usejoystick, 0},
    {"joyb_fire",&joybfire,0},
    {"joyb_strafe",&joybstrafe,1},
    {"joyb_use",&joybuse,3},
    {"joyb_speed",&joybspeed,2},

    {"screenblocks",&screenblocks, 9},
    {"detaillevel",&detailLevel, 0},

    {"snd_channels",&numChannels, 3},



    {"usegamma",&usegamma, 0},

    {"chatmacro0", (int *) &chat_macros[0], (int) HUSTR_CHATMACRO0 },
    {"chatmacro1", (int *) &chat_macros[1], (int) HUSTR_CHATMACRO1 },
    {"chatmacro2", (int *) &chat_macros[2], (int) HUSTR_CHATMACRO2 },
    {"chatmacro3", (int *) &chat_macros[3], (int) HUSTR_CHATMACRO3 },
    {"chatmacro4", (int *) &chat_macros[4], (int) HUSTR_CHATMACRO4 },
    {"chatmacro5", (int *) &chat_macros[5], (int) HUSTR_CHATMACRO5 },
    {"chatmacro6", (int *) &chat_macros[6], (int) HUSTR_CHATMACRO6 },
    {"chatmacro7", (int *) &chat_macros[7], (int) HUSTR_CHATMACRO7 },
    {"chatmacro8", (int *) &chat_macros[8], (int) HUSTR_CHATMACRO8 },
    {"chatmacro9", (int *) &chat_macros[9], (int) HUSTR_CHATMACRO9 }

};

int	numdefaults;
char*	defaultfile;


//
// M_SaveDefaults
//
void M_SaveDefaults (void)
{
#if 0		// AJH uGFX HACK
    int		i;
    int		v;
    FILE*	f;
	
    f = fopen (defaultfile, "w");
    if (!f)
	return; // can't write the file, but don't complain
		
    for (i=0 ; i<numdefaults ; i++)
    {
	if (defaults[i].defaultvalue > -0xfff
	    && defaults[i].defaultvalue < 0xfff)
	{
	    v = *defaults[i].location;
	    fprintf (f,"%s\t\t%i\n",defaults[i].name,v);
	} else {
	    fprintf (f,"%s\t\t\"%s\"\n",defaults[i].name,
		     * (char **) (defaults[i].location));
	}
    }
	
    fclose (f);
#endif
}


//
// M_LoadDefaults
//
extern byte	scantokey[128];

void M_LoadDefaults (void)
{
    int		i;
    int		len;
    FILE*	f;
    char	def[80];
    char	strparm[100];
    char*	newstring;
    int		parm;
    boolean	isstring;
    
    // set everything to base values
    numdefaults = sizeof(defaults)/sizeof(defaults[0]);
    for (i=0 ; i<numdefaults ; i++)
	*defaults[i].location = defaults[i].defaultvalue;
    
    // check for a custom default file
    i = M_CheckParm ("-config");
    if (i && i<myargc-1)
    {
	defaultfile = myargv[i+1];
	I_printf ("	default file: %s\n",defaultfile);
    }
    else
	defaultfile = basedefault;

#if 0	// AJH uGFX HACK
    // read the file in, overriding any set defaults
    f = fopen (defaultfile, "r");
    if (f)
    {
	while (!feof(f))
	{
	    isstring = false;
	    if (fscanf (f, "%79s %[^\n]\n", def, strparm) == 2)
	    {
		if (strparm[0] == '"')
		{
		    // get a string default
		    isstring = true;
		    len = strlen(strparm);
		    newstring = (char *) I_malloc(len);
		    strparm[len-1] = 0;
		    strcpy(newstring, strparm+1);
		}
		else if (strparm[0] == '0' && strparm[1] == 'x')
		    sscanf(strparm+2, "%x", &parm);
		else
		    sscanf(strparm, "%i", &parm);
		for (i=0 ; i<numdefaults ; i++)
		    if (!strcmp(def, defaults[i].name))
		    {
			if (!isstring)
			    *defaults[i].location = parm;
			else
			    *defaults[i].location =
				(int) newstring;
			break;
		    }
	    }
	}
		
	fclose (f);
    }
#endif
}


//
// SCREEN SHOTS
//


typedef struct
{
    char		manufacturer;
    char		version;
    char		encoding;
    char		bits_per_pixel;

    unsigned short	xmin;
    unsigned short	ymin;
    unsigned short	xmax;
    unsigned short	ymax;
    
    unsigned short	hres;
    unsigned short	vres;

    unsigned char	palette[48];
    
    char		reserved;
    char		color_planes;
    unsigned short	bytes_per_line;
    unsigned short	palette_type;
    
    char		filler[58];
    unsigned char	data;		// unbounded
} pcx_t;


//
// WritePCXfile
//
void
WritePCXfile
( char*		filename,
  byte*		data,
  int		width,
  int		height,
  byte*		palette )
{
    int		i;
    int		length;
    pcx_t*	pcx;
    byte*	pack;
	
    pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL);

    pcx->manufacturer = 0x0a;		// PCX id
    pcx->version = 5;			// 256 color
    pcx->encoding = 1;			// uncompressed
    pcx->bits_per_pixel = 8;		// 256 color
    pcx->xmin = 0;
    pcx->ymin = 0;
    pcx->xmax = SHORT(width-1);
    pcx->ymax = SHORT(height-1);
    pcx->hres = SHORT(width);
    pcx->vres = SHORT(height);
    memset (pcx->palette,0,sizeof(pcx->palette));
    pcx->color_planes = 1;		// chunky image
    pcx->bytes_per_line = SHORT(width);
    pcx->palette_type = SHORT(2);	// not a grey scale
    memset (pcx->filler,0,sizeof(pcx->filler));


    // pack the image
    pack = &pcx->data;
	
    for (i=0 ; i<width*height ; i++)
    {
	if ( (*data & 0xc0) != 0xc0)
	    *pack++ = *data++;
	else
	{
	    *pack++ = 0xc1;
	    *pack++ = *data++;
	}
    }
    
    // write the palette
    *pack++ = 0x0c;	// palette ID byte
    for (i=0 ; i<768 ; i++)
	*pack++ = *palette++;
    
    // write output file
    length = pack - (byte *)pcx;
    M_WriteFile (filename, pcx, length);

    Z_Free (pcx);
}


//
// M_ScreenShot
//
void M_ScreenShot (void)
{
    int		i;
    byte*	linear;
    char	lbmname[12];
    
    // munge planar buffer to linear
    linear = screens[2];
    I_ReadScreen (linear);
    
    // find a file name to save it to
    strcpy(lbmname,"DOOM00.pcx");
		
    for (i=0 ; i<=99 ; i++)
    {
	lbmname[4] = i/10 + '0';
	lbmname[5] = i%10 + '0';
	if (!I_HaveFile(lbmname))
	    break;	// file doesn't exist
    }
    if (i==100)
	I_Error ("M_ScreenShot: Couldn't create a PCX");
    
    // save the pcx file
    WritePCXfile (lbmname, linear,
		  SCREENWIDTH, SCREENHEIGHT,
		  W_CacheLumpName ("PLAYPAL",PU_CACHE));
	
    players[consoleplayer].message = "screen shot";
}