Initial commit
|
|
@ -0,0 +1,6 @@
|
||||||
|
.idea
|
||||||
|
*.o
|
||||||
|
*.gbm
|
||||||
|
gbmconv
|
||||||
|
game.exe
|
||||||
|
game.img
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
CC=/usr/local/djgpp/bin/i586-pc-msdosdjgpp-gcc
|
||||||
|
CXX=/usr/local/djgpp/bin/i586-pc-msdosdjgpp-g++
|
||||||
|
LD=/usr/local/djgpp/bin/i586-pc-msdosdjgpp-g++
|
||||||
|
STRIP=i586-pc-msdosdjgpp-strip
|
||||||
|
|
||||||
|
CFLAGS=-O3 -march=i486
|
||||||
|
CXXFLAGS=$(CFLAGS)
|
||||||
|
LDFLAGS=
|
||||||
|
|
||||||
|
EXE=game.exe
|
||||||
|
OBJ=main.o
|
||||||
|
OBJ+=system/Timer.o system/Video.o system/Keyboard.o
|
||||||
|
OBJ+=graphics/Bitmap.o
|
||||||
|
OBJ+=audio/rad20player.o audio/AudioPlayer.o audio/Music.o audio/Audio.o
|
||||||
|
OBJ+=util/Log.o util/Files.o util/Bmp.o util/Gbm.o
|
||||||
|
OBJ+=scenes/Scene.o scenes/IntroScene.o scenes/MainMenuScene.o scenes/GameScene.o
|
||||||
|
|
||||||
|
CONVERTER_CXX=g++
|
||||||
|
CONVERTER_EXE=gbmconv
|
||||||
|
CONVERTER_SRC=converter.cpp
|
||||||
|
CONVERTER_SRC+=graphics/Bitmap.cpp
|
||||||
|
CONVERTER_SRC+=util/Files.cpp util/Bmp.cpp util/Gbm.cpp
|
||||||
|
|
||||||
|
COMPILED_GFX_ASSETS=assets/font1.gbm assets/cow.gbm assets/witch.gbm
|
||||||
|
GFX_ASSETS=assets/bg.bmp $(COMPILED_GFX_ASSETS)
|
||||||
|
MUSIC_ASSETS=assets/rain.rad assets/getup.rad assets/spiral.rad
|
||||||
|
EXTRA_FILES=install.bat
|
||||||
|
DPMI_HOST=CWSDPMI.EXE
|
||||||
|
|
||||||
|
RELEASE_FILES=$(EXE) $(DPMI_HOST) $(GFX_ASSETS) $(MUSIC_ASSETS) $(EXTRA_FILES)
|
||||||
|
|
||||||
|
FLOPPY_IMG=game.img
|
||||||
|
|
||||||
|
.PHONY: all clean release assets floppy
|
||||||
|
|
||||||
|
all: $(CONVERTER_EXE) $(EXE)
|
||||||
|
|
||||||
|
clean: ; rm -rf $(OBJ) $(EXE) $(CONVERTER_EXE) $(FLOPPY_IMG) $(COMPILED_GFX_ASSETS) release
|
||||||
|
|
||||||
|
release: all assets; $(STRIP) $(EXE) && upx $(EXE); mkdir release; cp -Rv $(RELEASE_FILES) release/
|
||||||
|
|
||||||
|
floppy: release; dd if=/dev/zero of=$(FLOPPY_IMG) bs=512 count=2880 && mkfs.fat -F12 $(FLOPPY_IMG) && mcopy -i $(FLOPPY_IMG) -s release/* ::
|
||||||
|
|
||||||
|
$(CONVERTER_EXE): $(CONVERTER_SRC); $(CONVERTER_CXX) -o $@ $(CONVERTER_SRC)
|
||||||
|
|
||||||
|
$(EXE): $(OBJ); $(LD) $(LDFLAGS) -o $@ $(OBJ)
|
||||||
|
|
||||||
|
%.o : %.c ; $(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.o : %.cpp ; $(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
assets: $(CONVERTER_EXE) $(GFX_ASSETS)
|
||||||
|
|
||||||
|
%.gbm : %.bmp %_m.bmp ; ./$(CONVERTER_EXE) $@ $^
|
||||||
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "Audio.h"
|
||||||
|
#include "../util/Files.h"
|
||||||
|
|
||||||
|
#include "AudioPlayer.h"
|
||||||
|
|
||||||
|
Audio::Audio() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio::Audio(const char *path) {
|
||||||
|
loadFromFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio::~Audio() {
|
||||||
|
unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Audio::isValid() const {
|
||||||
|
return _data != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::loadFromFile(const char *path) {
|
||||||
|
unload();
|
||||||
|
_length = Files::allocateBufferAndLoadFromFile(path, &_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::unload() {
|
||||||
|
if (_data) {
|
||||||
|
if (audioPlayer.isPlaying(*this)) {
|
||||||
|
audioPlayer.stopAudio(*this);
|
||||||
|
}
|
||||||
|
Files::deleteBuffer(_data);
|
||||||
|
_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef GAME_AUDIO_H
|
||||||
|
#define GAME_AUDIO_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
class Audio {
|
||||||
|
Audio();
|
||||||
|
explicit Audio(const char *path);
|
||||||
|
~Audio();
|
||||||
|
|
||||||
|
[[nodiscard]] bool isValid() const;
|
||||||
|
void loadFromFile(const char* path);
|
||||||
|
void unload();
|
||||||
|
private:
|
||||||
|
friend class AudioPlayer;
|
||||||
|
[[nodiscard]] void *getData() const { return _data; }
|
||||||
|
size_t _length{0};
|
||||||
|
void *_data{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_AUDIO_H
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include <go32.h>
|
||||||
|
|
||||||
|
#include "AudioPlayer.h"
|
||||||
|
|
||||||
|
#include "../system/Timer.h"
|
||||||
|
#include "../util/Asm.h"
|
||||||
|
|
||||||
|
#define DMA_BUFFER_SIZE (SB_BUFFER_SIZE * 4)
|
||||||
|
|
||||||
|
AudioPlayer audioPlayer;
|
||||||
|
|
||||||
|
void radPlayerWriteReg(void *p, uint16_t reg, uint8_t data) {
|
||||||
|
auto player = static_cast<AudioPlayer *>(p);
|
||||||
|
player->writeOpl(reg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioPlayerOnTimer() {
|
||||||
|
audioPlayer.onTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioPlayer::AudioPlayer() {
|
||||||
|
// Allocate twice the amount needed and select one half depending on whether it crosses a 64k boundary
|
||||||
|
_dmaBuffer.size = (DMA_BUFFER_SIZE + 15) >> (4-1);
|
||||||
|
_go32_dpmi_allocate_dos_memory(&_dmaBuffer);
|
||||||
|
|
||||||
|
auto physFirst = (_dmaBuffer.rm_segment << 4) + _dmaBuffer.rm_offset;
|
||||||
|
auto physSecond = physFirst + DMA_BUFFER_SIZE;
|
||||||
|
auto pageFirst = physFirst >> 16;
|
||||||
|
auto pageSecond = physSecond >> 16;
|
||||||
|
|
||||||
|
if (pageFirst == pageSecond) {
|
||||||
|
// Use first half
|
||||||
|
_buffers = reinterpret_cast<decltype(_buffers)>(physFirst);
|
||||||
|
_dmaPage = pageFirst;
|
||||||
|
_dmaOffset = physFirst & 0xFFFF;
|
||||||
|
} else {
|
||||||
|
// Use second half
|
||||||
|
_buffers = reinterpret_cast<decltype(_buffers)>(physSecond);
|
||||||
|
_dmaPage = pageSecond;
|
||||||
|
_dmaOffset = physSecond & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.setCallback(audioPlayerOnTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioPlayer::~AudioPlayer() {
|
||||||
|
timer.setCallback(nullptr);
|
||||||
|
_go32_dpmi_free_dos_memory(&_dmaBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::playMusic(Music &music) {
|
||||||
|
stopMusic();
|
||||||
|
|
||||||
|
_currentMusic = &music;
|
||||||
|
_musicPlayer.Init(_currentMusic->getData(), radPlayerWriteReg, this);
|
||||||
|
|
||||||
|
auto hz = _musicPlayer.GetHertz();
|
||||||
|
if (hz < 0) {
|
||||||
|
stopMusic();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start music playback
|
||||||
|
timer.setFrequency(hz);
|
||||||
|
resumeMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::stopMusic() {
|
||||||
|
if (!_currentMusic) return;
|
||||||
|
|
||||||
|
_musicPlayer.Stop();
|
||||||
|
_paused = true;
|
||||||
|
_currentMusic = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::pauseMusic() {
|
||||||
|
_paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::resumeMusic() {
|
||||||
|
if (!_currentMusic) return;
|
||||||
|
|
||||||
|
_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::playAudio(Audio &audio, int priority, int flags) {
|
||||||
|
if (priority > AUDIO_MAX_PRIORITY) priority = AUDIO_MAX_PRIORITY;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioPlayer::isPlaying(const Audio &audio) {
|
||||||
|
for (auto &channel : _channels) {
|
||||||
|
if (channel.audio == &audio) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::stopAudio(Audio &audio) {
|
||||||
|
for (auto &channel : _channels) {
|
||||||
|
if (channel.audio == &audio) {
|
||||||
|
channel.audio = nullptr;
|
||||||
|
channel.loop = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::stopAllAudio() {
|
||||||
|
for (auto &channel : _channels) {
|
||||||
|
channel.audio = nullptr;
|
||||||
|
channel.loop = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioPlayer::isPlaying(const Music &music) const {
|
||||||
|
return _currentMusic == &music;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::writeOpl(uint16_t reg, uint8_t data) const {
|
||||||
|
if (reg >= 0x100) {
|
||||||
|
outb(_oplReg + 2, reg & 0xff);
|
||||||
|
outb(_oplReg + 3, data);
|
||||||
|
} else {
|
||||||
|
outb(_oplReg, reg);
|
||||||
|
outb(_oplReg + 1, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::onTimer() {
|
||||||
|
if (_paused) return;
|
||||||
|
if (!_currentMusic) return;
|
||||||
|
|
||||||
|
_musicPlayer.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::generateSamples() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::copySamples() {
|
||||||
|
// No data available - should not happen, but let's force generate a block
|
||||||
|
if (_nextBufferReadIndex == _nextBufferWriteIndex) {
|
||||||
|
generateSamples();
|
||||||
|
}
|
||||||
|
|
||||||
|
dosmemput(_buffers, SB_BUFFER_SIZE, (_dmaBuffer.rm_segment << 4) + _nextBufferReadIndex * SB_BUFFER_SIZE);
|
||||||
|
_nextBufferReadIndex = (_nextBufferReadIndex + 1) & 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef GAME_AUDIOPLAYER_H
|
||||||
|
#define GAME_AUDIOPLAYER_H
|
||||||
|
|
||||||
|
#include <dpmi.h>
|
||||||
|
|
||||||
|
#include "Audio.h"
|
||||||
|
#include "Music.h"
|
||||||
|
#include "rad20player.h"
|
||||||
|
|
||||||
|
#define SB_BUFFER_SIZE 1024
|
||||||
|
#define SB_BUFFERS 32
|
||||||
|
|
||||||
|
#define AUDIO_CHANNELS 8
|
||||||
|
#define AUDIO_MAX_PRIORITY 7
|
||||||
|
|
||||||
|
#define AUDIO_PLAY_LOOP 0x01
|
||||||
|
#define AUDIO_PLAY_FIXED_PRIORITY 0x02
|
||||||
|
|
||||||
|
class AudioPlayer {
|
||||||
|
public:
|
||||||
|
AudioPlayer();
|
||||||
|
|
||||||
|
~AudioPlayer();
|
||||||
|
|
||||||
|
void playMusic(Music &music);
|
||||||
|
|
||||||
|
void stopMusic();
|
||||||
|
|
||||||
|
void pauseMusic();
|
||||||
|
|
||||||
|
void resumeMusic();
|
||||||
|
|
||||||
|
void playAudio(Audio &audio, int priority, int flags = 0);
|
||||||
|
|
||||||
|
bool isPlaying(const Audio & audio);
|
||||||
|
|
||||||
|
void stopAudio(Audio &audio);
|
||||||
|
|
||||||
|
void stopAllAudio();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Music;
|
||||||
|
friend void radPlayerWriteReg(void *p, uint16_t reg, uint8_t data);
|
||||||
|
friend void audioPlayerOnTimer();
|
||||||
|
|
||||||
|
RADPlayer _musicPlayer;
|
||||||
|
Music *_currentMusic{nullptr};
|
||||||
|
bool _paused{true};
|
||||||
|
uint16_t _oplReg{0x388};
|
||||||
|
_go32_dpmi_seginfo _dmaBuffer{};
|
||||||
|
uint8_t (*_buffers)[SB_BUFFER_SIZE]{};
|
||||||
|
uint8_t _dmaPage{0};
|
||||||
|
uint16_t _dmaOffset{0};
|
||||||
|
unsigned _nextBufferReadIndex{0};
|
||||||
|
unsigned _nextBufferWriteIndex{0};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
Audio *audio{nullptr};
|
||||||
|
bool loop{false};
|
||||||
|
} _channels[AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
[[nodiscard]] bool isPlaying(const Music &music) const;
|
||||||
|
void writeOpl(uint16_t reg, uint8_t data) const;
|
||||||
|
void onTimer();
|
||||||
|
|
||||||
|
void generateSamples();
|
||||||
|
|
||||||
|
void copySamples();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern AudioPlayer audioPlayer;
|
||||||
|
|
||||||
|
#endif //GAME_AUDIOPLAYER_H
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "Music.h"
|
||||||
|
|
||||||
|
#include "AudioPlayer.h"
|
||||||
|
#include "../util/Files.h"
|
||||||
|
|
||||||
|
Music::Music() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Music::Music(const char *path) {
|
||||||
|
loadFromFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Music::~Music() {
|
||||||
|
unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Music::isValid() const {
|
||||||
|
return _data != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Music::loadFromFile(const char *path) {
|
||||||
|
unload();
|
||||||
|
_length = Files::allocateBufferAndLoadFromFile(path, &_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Music::unload() {
|
||||||
|
if (_data) {
|
||||||
|
if (audioPlayer.isPlaying(*this)) {
|
||||||
|
audioPlayer.stopMusic();
|
||||||
|
}
|
||||||
|
Files::deleteBuffer(_data);
|
||||||
|
_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef GAME_MUSIC_H
|
||||||
|
#define GAME_MUSIC_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
class Music {
|
||||||
|
public:
|
||||||
|
Music();
|
||||||
|
explicit Music(const char *path);
|
||||||
|
~Music();
|
||||||
|
|
||||||
|
[[nodiscard]] bool isValid() const;
|
||||||
|
void loadFromFile(const char* path);
|
||||||
|
void unload();
|
||||||
|
private:
|
||||||
|
friend class AudioPlayer;
|
||||||
|
[[nodiscard]] void *getData() const { return _data; }
|
||||||
|
size_t _length{0};
|
||||||
|
void *_data{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_MUSIC_H
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
#ifndef RAD20PLAYER_H
|
||||||
|
#define RAD20PLAYER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// RAD player class.
|
||||||
|
//==================================================================================================
|
||||||
|
class RADPlayer {
|
||||||
|
|
||||||
|
// Various constants
|
||||||
|
enum {
|
||||||
|
kTracks = 100,
|
||||||
|
kChannels = 9,
|
||||||
|
kTrackLines = 64,
|
||||||
|
kRiffTracks = 10,
|
||||||
|
kInstruments = 127,
|
||||||
|
|
||||||
|
cmPortamentoUp = 0x1,
|
||||||
|
cmPortamentoDwn = 0x2,
|
||||||
|
cmToneSlide = 0x3,
|
||||||
|
cmToneVolSlide = 0x5,
|
||||||
|
cmVolSlide = 0xA,
|
||||||
|
cmSetVol = 0xC,
|
||||||
|
cmJumpToLine = 0xD,
|
||||||
|
cmSetSpeed = 0xF,
|
||||||
|
cmIgnore = ('I' - 55),
|
||||||
|
cmMultiplier = ('M' - 55),
|
||||||
|
cmRiff = ('R' - 55),
|
||||||
|
cmTranspose = ('T' - 55),
|
||||||
|
cmFeedback = ('U' - 55),
|
||||||
|
cmVolume = ('V' - 55),
|
||||||
|
};
|
||||||
|
|
||||||
|
enum e_Source {
|
||||||
|
SNone, SRiff, SIRiff,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
fKeyOn = 1 << 0,
|
||||||
|
fKeyOff = 1 << 1,
|
||||||
|
fKeyedOn = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CInstrument {
|
||||||
|
uint8_t Feedback[2];
|
||||||
|
uint8_t Panning[2];
|
||||||
|
uint8_t Algorithm;
|
||||||
|
uint8_t Detune;
|
||||||
|
uint8_t Volume;
|
||||||
|
uint8_t RiffSpeed;
|
||||||
|
uint8_t * Riff;
|
||||||
|
uint8_t Operators[4][5];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CEffects {
|
||||||
|
int8_t PortSlide;
|
||||||
|
int8_t VolSlide;
|
||||||
|
uint16_t ToneSlideFreq;
|
||||||
|
uint8_t ToneSlideOct;
|
||||||
|
uint8_t ToneSlideSpeed;
|
||||||
|
int8_t ToneSlideDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CChannel {
|
||||||
|
uint8_t LastInstrument;
|
||||||
|
CInstrument * Instrument;
|
||||||
|
uint8_t Volume;
|
||||||
|
uint8_t DetuneA;
|
||||||
|
uint8_t DetuneB;
|
||||||
|
uint8_t KeyFlags;
|
||||||
|
uint16_t CurrFreq;
|
||||||
|
int8_t CurrOctave;
|
||||||
|
CEffects FX;
|
||||||
|
struct CRiff {
|
||||||
|
CEffects FX;
|
||||||
|
uint8_t * Track;
|
||||||
|
uint8_t * TrackStart;
|
||||||
|
uint8_t Line;
|
||||||
|
uint8_t Speed;
|
||||||
|
uint8_t SpeedCnt;
|
||||||
|
int8_t TransposeOctave;
|
||||||
|
int8_t TransposeNote;
|
||||||
|
uint8_t LastInstrument;
|
||||||
|
} Riff, IRiff;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
RADPlayer() : Initialised(false) {}
|
||||||
|
void Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t), void *arg);
|
||||||
|
void Stop();
|
||||||
|
bool Update();
|
||||||
|
int GetHertz() const { return Hertz; }
|
||||||
|
int GetPlayTimeInSeconds() const { return PlayTime / Hertz; }
|
||||||
|
int GetTunePos() const { return Order; }
|
||||||
|
int GetTuneLength() const { return OrderListSize; }
|
||||||
|
int GetTuneLine() const { return Line; }
|
||||||
|
void SetMasterVolume(int vol) { MasterVol = vol; }
|
||||||
|
int GetMasterVolume() const { return MasterVol; }
|
||||||
|
int GetSpeed() const { return Speed; }
|
||||||
|
|
||||||
|
#if RAD_DETECT_REPEATS
|
||||||
|
uint32_t ComputeTotalTime();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool UnpackNote(uint8_t *&s, uint8_t &last_instrument);
|
||||||
|
uint8_t * GetTrack();
|
||||||
|
uint8_t * SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff = false);
|
||||||
|
void PlayLine();
|
||||||
|
void PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t instnum, uint8_t cmd = 0, uint8_t param = 0, e_Source src = SNone, int op = 0);
|
||||||
|
void LoadInstrumentOPL3(int channum);
|
||||||
|
void PlayNoteOPL3(int channum, int8_t octave, int8_t note);
|
||||||
|
void ResetFX(CEffects *fx);
|
||||||
|
void TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff);
|
||||||
|
void ContinueFX(int channum, CEffects *fx);
|
||||||
|
void SetVolume(int channum, uint8_t vol);
|
||||||
|
void GetSlideDir(int channum, CEffects *fx);
|
||||||
|
void LoadInstMultiplierOPL3(int channum, int op, uint8_t mult);
|
||||||
|
void LoadInstVolumeOPL3(int channum, int op, uint8_t vol);
|
||||||
|
void LoadInstFeedbackOPL3(int channum, int which, uint8_t fb);
|
||||||
|
void Portamento(uint16_t channum, CEffects *fx, int8_t amount, bool toneslide);
|
||||||
|
void Transpose(int8_t note, int8_t octave);
|
||||||
|
void SetOPL3(uint16_t reg, uint8_t val) {
|
||||||
|
OPL3Regs[reg] = val;
|
||||||
|
OPL3(OPL3Arg, reg, val);
|
||||||
|
}
|
||||||
|
uint8_t GetOPL3(uint16_t reg) const {
|
||||||
|
return OPL3Regs[reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
void (*OPL3)(void *, uint16_t, uint8_t);
|
||||||
|
void * OPL3Arg;
|
||||||
|
CInstrument Instruments[kInstruments];
|
||||||
|
CChannel Channels[kChannels];
|
||||||
|
uint32_t PlayTime;
|
||||||
|
#if RAD_DETECT_REPEATS
|
||||||
|
uint32_t OrderMap[4];
|
||||||
|
bool Repeating;
|
||||||
|
#endif
|
||||||
|
int16_t Hertz;
|
||||||
|
uint8_t * OrderList;
|
||||||
|
uint8_t * Tracks[kTracks];
|
||||||
|
uint8_t * Riffs[kRiffTracks][kChannels];
|
||||||
|
uint8_t * Track;
|
||||||
|
bool Initialised;
|
||||||
|
uint8_t Speed;
|
||||||
|
uint8_t OrderListSize;
|
||||||
|
uint8_t SpeedCnt;
|
||||||
|
uint8_t Order;
|
||||||
|
uint8_t Line;
|
||||||
|
int8_t Entrances;
|
||||||
|
uint8_t MasterVol;
|
||||||
|
int8_t LineJump;
|
||||||
|
uint8_t OPL3Regs[512];
|
||||||
|
|
||||||
|
// Values exported by UnpackNote()
|
||||||
|
int8_t NoteNum;
|
||||||
|
int8_t OctaveNum;
|
||||||
|
uint8_t InstNum;
|
||||||
|
uint8_t EffectNum;
|
||||||
|
uint8_t Param;
|
||||||
|
bool LastNote;
|
||||||
|
|
||||||
|
static const int8_t NoteSize[];
|
||||||
|
static const uint16_t ChanOffsets3[9], Chn2Offsets3[9];
|
||||||
|
static const uint16_t NoteFreq[];
|
||||||
|
static const uint16_t OpOffsets3[9][4];
|
||||||
|
static const bool AlgCarriers[7][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "util/Bmp.h"
|
||||||
|
#include "util/Gbm.h"
|
||||||
|
|
||||||
|
void usage() {
|
||||||
|
printf("Usage: gbmconv <gbm output file> <source bitmap> <source mask bitmap>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc < 4) {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *gbmPath = argv[1];
|
||||||
|
char *bmpPath = argv[2];
|
||||||
|
char *maskBmpPath = argv[3];
|
||||||
|
|
||||||
|
auto bitmap = Bmp::loadFromFile(bmpPath);
|
||||||
|
if (bitmap.GetWidth() == 0 || bitmap.GetHeight() == 0) {
|
||||||
|
printf("Unable to load source bitmap\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto maskBitmap = Bmp::loadFromFile(maskBmpPath);
|
||||||
|
if (maskBitmap.GetWidth() == 0 || maskBitmap.GetHeight() == 0) {
|
||||||
|
printf("Unable to load mask bitmap\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maskBitmap.GetWidth() != bitmap.GetWidth() || maskBitmap.GetHeight() != bitmap.GetHeight()) {
|
||||||
|
printf("Mask bitmap must be the same size as the source bitmap\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a combined bitmap where the most significant bit is a transparency bit
|
||||||
|
auto combined = Bitmap(bitmap.GetWidth(), bitmap.GetHeight());
|
||||||
|
for (auto y = 0u; y < bitmap.GetHeight(); y++) {
|
||||||
|
for (auto x = 0u; x < bitmap.GetWidth(); x++) {
|
||||||
|
auto isTransparent = (maskBitmap.GetPixel(x, y) == 0);
|
||||||
|
auto pv = (isTransparent ? 0x80 : 0x00) | (bitmap.GetPixel(x, y) & 0x7f);
|
||||||
|
combined.SetPixel(x, y, pv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto i = 0u; i < 256; i++) {
|
||||||
|
combined.SetPaletteEntry(i, bitmap.GetPaletteEntry(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
Gbm::writeToFile(gbmPath, combined);
|
||||||
|
printf("Wrote %s\n", gbmPath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include "Bitmap.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
Bitmap::~Bitmap() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(const Bitmap &other) {
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap & Bitmap::operator=(const Bitmap &other) {
|
||||||
|
if (this != &other) {
|
||||||
|
clear();
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(Bitmap &&other) noexcept {
|
||||||
|
moveFrom(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap & Bitmap::operator=(Bitmap &&other) noexcept {
|
||||||
|
moveFrom(std::move(other));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(uint16_t width, uint16_t height)
|
||||||
|
: _width(width), _height(height), _data(new uint8_t[width * height]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(uint16_t width, uint16_t height, uint8_t *data) : Bitmap(width, height) {
|
||||||
|
memcpy(_data, data, width * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::SetPaletteEntry(uint8_t index, Video::PaletteEntry entry) {
|
||||||
|
_pal[index] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Video::PaletteEntry Bitmap::GetPaletteEntry(uint8_t index) const {
|
||||||
|
return _pal[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::clear() {
|
||||||
|
delete[] _data;
|
||||||
|
_data = nullptr;
|
||||||
|
_width = _height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::copyFrom(const Bitmap &other) {
|
||||||
|
_width = other._width;
|
||||||
|
_height = other._height;
|
||||||
|
_data = new uint8_t[_width * _height];
|
||||||
|
for (auto i = 0; i < 256; i++) _pal[i] = other._pal[i];
|
||||||
|
|
||||||
|
memcpy(_data, other._data, _width * _height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::moveFrom(Bitmap &&other) noexcept {
|
||||||
|
// Clear existing data
|
||||||
|
clear();
|
||||||
|
|
||||||
|
// Copy data from other
|
||||||
|
_data = other._data;
|
||||||
|
_width = other._width;
|
||||||
|
_height = other._height;
|
||||||
|
for (auto i = 0; i < 256; i++) _pal[i] = other._pal[i];
|
||||||
|
|
||||||
|
// Clear other
|
||||||
|
other._data = nullptr;
|
||||||
|
other._width = other._height = 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef GAME_BITMAP_H
|
||||||
|
#define GAME_BITMAP_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../system/Video.h"
|
||||||
|
|
||||||
|
class Bitmap {
|
||||||
|
public:
|
||||||
|
Bitmap() = default;
|
||||||
|
|
||||||
|
~Bitmap();
|
||||||
|
|
||||||
|
Bitmap(const Bitmap& other);
|
||||||
|
Bitmap& operator=(const Bitmap& other);
|
||||||
|
|
||||||
|
Bitmap(Bitmap &&other) noexcept ;
|
||||||
|
Bitmap& operator=(Bitmap &&other) noexcept;
|
||||||
|
|
||||||
|
Bitmap(uint16_t width, uint16_t height);
|
||||||
|
|
||||||
|
Bitmap(uint16_t width, uint16_t height, uint8_t* data);
|
||||||
|
|
||||||
|
[[nodiscard]] uint8_t GetPixel(uint16_t x, uint16_t y) const {
|
||||||
|
return _data[y * _width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPixel(uint16_t x, uint16_t y, uint8_t value) {
|
||||||
|
_data[y * _width + x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t &operator[](uint32_t offset) {
|
||||||
|
return _data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t &operator[](uint32_t offset) const {
|
||||||
|
return _data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPaletteEntry(uint8_t index, Video::PaletteEntry entry);
|
||||||
|
|
||||||
|
[[nodiscard]] Video::PaletteEntry GetPaletteEntry(uint8_t index) const;
|
||||||
|
|
||||||
|
[[nodiscard]] uint16_t GetWidth() const {
|
||||||
|
return _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] uint16_t GetHeight() const {
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t _width{0};
|
||||||
|
uint16_t _height{0};
|
||||||
|
uint8_t *_data{nullptr};
|
||||||
|
|
||||||
|
Video::PaletteEntry _pal[256]{};
|
||||||
|
|
||||||
|
void copyFrom(const Bitmap& other);
|
||||||
|
void moveFrom(Bitmap&& other) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_BITMAP_H
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#ifndef GAME_FONT_H
|
||||||
|
#define GAME_FONT_H
|
||||||
|
|
||||||
|
#include "Bitmap.h"
|
||||||
|
|
||||||
|
class Font {
|
||||||
|
public:
|
||||||
|
Font(Bitmap *bitmap, unsigned w, unsigned h, unsigned minCode = 32, unsigned maxCode = 127)
|
||||||
|
: _font(bitmap), _w(w), _h(h), _minCode(minCode), _maxCode(maxCode) {
|
||||||
|
_charsPerRow = _font->GetWidth() / _w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderCharAlpha(int c, int x, int y, uint8_t color, uint8_t *fb, unsigned fbWidth, unsigned fbHeight) {
|
||||||
|
if (c < _minCode || c > _maxCode) return;
|
||||||
|
c -= _minCode;
|
||||||
|
|
||||||
|
auto bitmapX = (c % _charsPerRow) * _w;
|
||||||
|
auto bitmapY = (c / _charsPerRow) * _h;
|
||||||
|
|
||||||
|
for (auto charY = 0; charY < _h; charY++) {
|
||||||
|
if (y + charY < 0) continue;
|
||||||
|
if (y + charY >= fbHeight) break;
|
||||||
|
auto yOffset = (y + charY) * fbWidth;
|
||||||
|
|
||||||
|
for (auto charX = 0; charX < _w; charX++) {
|
||||||
|
if (x + charX < 0) continue;
|
||||||
|
if (x + charX >= fbWidth) break;
|
||||||
|
|
||||||
|
auto pv = _font->GetPixel(bitmapX + charX, bitmapY + charY);
|
||||||
|
if (pv & 0x80) continue;
|
||||||
|
|
||||||
|
fb[yOffset + x + charX] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderChar(int c, int x, int y, uint8_t fgColor, uint8_t bgColor, uint8_t *fb, unsigned fbWidth, unsigned fbHeight) {
|
||||||
|
if (c < _minCode || c > _maxCode) return;
|
||||||
|
c -= _minCode;
|
||||||
|
|
||||||
|
auto bitmapX = (c % _charsPerRow) * _w;
|
||||||
|
auto bitmapY = (c / _charsPerRow) * _h;
|
||||||
|
|
||||||
|
for (auto charY = 0; charY < _h; charY++) {
|
||||||
|
if (y + charY < 0) continue;
|
||||||
|
if (y + charY >= fbHeight) break;
|
||||||
|
auto yOffset = (y + charY) * fbWidth;
|
||||||
|
|
||||||
|
for (auto charX = 0; charX < _w; charX++) {
|
||||||
|
if (x + charX < 0) continue;
|
||||||
|
if (x + charX >= fbWidth) break;
|
||||||
|
|
||||||
|
auto pv = _font->GetPixel(bitmapX + charX, bitmapY + charY) & 0x80 ? bgColor : fgColor;
|
||||||
|
fb[yOffset + x + charX] = pv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Bitmap *_font;
|
||||||
|
unsigned _w, _h;
|
||||||
|
unsigned _minCode;
|
||||||
|
unsigned _maxCode;
|
||||||
|
unsigned _charsPerRow{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_FONT_H
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef GAME_RECT_H
|
||||||
|
#define GAME_RECT_H
|
||||||
|
|
||||||
|
struct Rect {
|
||||||
|
int x1, y1, x2, y2;
|
||||||
|
|
||||||
|
void clamp(int minX, int minY, int maxX, int maxY) {
|
||||||
|
if (x1 < minX) x1 = minX;
|
||||||
|
if (x2 > maxX) x2 = maxX;
|
||||||
|
if (y1 < minY) y1 = minY;
|
||||||
|
if (y2 > maxY) y2 = maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort() {
|
||||||
|
if (x1 > x2) {
|
||||||
|
auto x = x1;
|
||||||
|
x1 = x2;
|
||||||
|
x2 = x;
|
||||||
|
}
|
||||||
|
if (y1 > y2) {
|
||||||
|
auto y = y1;
|
||||||
|
y1 = y2;
|
||||||
|
y2 = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect &operator+=(const Rect &other) {
|
||||||
|
if (other.x1 < x1) x1 = other.x1;
|
||||||
|
if (other.x2 > x2) x2 = other.x2;
|
||||||
|
if (other.y1 < y1) y1 = other.y1;
|
||||||
|
if (other.y2 > y2) y2 = other.y2;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool contains(int x, int y) const {
|
||||||
|
return x >= x1 && y >= y1 && x < x2 && y < y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool overlaps(const Rect &other) const {
|
||||||
|
return !(other.x1 > x2 || other.x2 < x1 || other.y1 > y2 || other.y2 < y1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //GAME_RECT_H
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#ifndef GAME_SPRITE_H
|
||||||
|
#define GAME_SPRITE_H
|
||||||
|
#include "Bitmap.h"
|
||||||
|
|
||||||
|
#define SPR_SUBPX_TO_PX(v) ((v) >= 0 ? ((v) >> 8) : -((-(v)) >> 8))
|
||||||
|
#define SPR_PX_TO_SUBPX(v) ((v) >= 0 ? ((v) << 8) : -((-(v)) << 8))
|
||||||
|
|
||||||
|
class Sprite {
|
||||||
|
public:
|
||||||
|
virtual ~Sprite() = default;
|
||||||
|
|
||||||
|
virtual void Tick() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual int GetWidth() const {
|
||||||
|
return _w;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] virtual int GetHeight() const {
|
||||||
|
return _h;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int GetX() const {
|
||||||
|
return SPR_SUBPX_TO_PX(_sx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int GetY() const {
|
||||||
|
return SPR_SUBPX_TO_PX(_sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetX(int x) {
|
||||||
|
_sx = x >= 0 ? (x << 8) : -((-x) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetY(int y) {
|
||||||
|
_sy = y >= 0 ? (y << 8) : -((-y) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Rect GetRect() const {
|
||||||
|
auto x = SPR_SUBPX_TO_PX(_sx), y = SPR_SUBPX_TO_PX(_sy);
|
||||||
|
return { x, y, x + _w, y + _h };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPalette(uint8_t palette) { _palette = palette << 4; }
|
||||||
|
|
||||||
|
[[nodiscard]] uint8_t GetPalette() const { return _palette >> 4; }
|
||||||
|
|
||||||
|
[[nodiscard]] Bitmap *GetBitmap() const { return _bitmap; }
|
||||||
|
|
||||||
|
[[nodiscard]] unsigned GetYOffset() const { return _frame * _h; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool isMirroredX() const { return _mirrorX; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool isMirroredY() const { return _mirrorY; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Sprite() = default;
|
||||||
|
|
||||||
|
Bitmap *_bitmap{nullptr};
|
||||||
|
uint8_t _palette{0};
|
||||||
|
int _sx{0}, _sy{0};
|
||||||
|
int _w{0}, _h{0};
|
||||||
|
unsigned _frame{0};
|
||||||
|
bool _mirrorX{false};
|
||||||
|
bool _mirrorY{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_SPRITE_H
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
mkdir C:\game\
|
||||||
|
xcopy *.* C:\game\
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <sys/nearptr.h>
|
||||||
|
|
||||||
|
#include "audio/AudioPlayer.h"
|
||||||
|
#include "audio/Music.h"
|
||||||
|
#include "system/Timer.h"
|
||||||
|
#include "system/Video.h"
|
||||||
|
#include "graphics/Bitmap.h"
|
||||||
|
#include "graphics/Font.h"
|
||||||
|
|
||||||
|
#include "scenes/Scene.h"
|
||||||
|
#include "system/Keyboard.h"
|
||||||
|
#include "util/Files.h"
|
||||||
|
#include "util/Gbm.h"
|
||||||
|
#include "util/Log.h"
|
||||||
|
|
||||||
|
volatile bool showFps = true;
|
||||||
|
volatile bool running = true;
|
||||||
|
|
||||||
|
uint8_t *loadMusicFile(const char *filename) {
|
||||||
|
void *data = nullptr;
|
||||||
|
Files::allocateBufferAndLoadFromFile(filename, &data);
|
||||||
|
return static_cast<uint8_t*>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
__djgpp_nearptr_enable();
|
||||||
|
srand(time(nullptr));
|
||||||
|
|
||||||
|
DefaultLog.log("Loading music\n");
|
||||||
|
Music music("spiral.rad");
|
||||||
|
if (!music.isValid()) {
|
||||||
|
printf("Unable to load song\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLog.log("Playing music\n");
|
||||||
|
audioPlayer.playMusic(music);
|
||||||
|
|
||||||
|
DefaultLog.log("Changing video mode\n");
|
||||||
|
video.Enter();
|
||||||
|
|
||||||
|
// Run scenes
|
||||||
|
auto fb = static_cast<uint8_t *>(video.GetFB());
|
||||||
|
|
||||||
|
unsigned fps{0};
|
||||||
|
unsigned frameCount{0};
|
||||||
|
uint32_t fpsTick{0};
|
||||||
|
char fpsDigits[3]{'0', '0', 0};
|
||||||
|
auto fontBitmap = Gbm::loadFromFile("font1.gbm");
|
||||||
|
Font font(&fontBitmap, 7, 9, 32, 127);
|
||||||
|
|
||||||
|
keyboard.setKeyDownHandler([] (unsigned char key) {
|
||||||
|
DefaultLog.log("KeyDown %u (%s)\n", key, keyboard.getKeyName(key));
|
||||||
|
});
|
||||||
|
keyboard.setKeyUpHandler([] (unsigned char key) {
|
||||||
|
DefaultLog.log("KeyUp %u (%s)\n", key, keyboard.getKeyName(key));
|
||||||
|
switch (key) {
|
||||||
|
case KeyEscape:
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
|
case KeyF:
|
||||||
|
showFps = !showFps;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
keyboard.setKeyRepeatHandler([](unsigned char key) {
|
||||||
|
DefaultLog.log("KeyRepeat %u (%s)\n", key, keyboard.getKeyName(key));
|
||||||
|
});
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
auto scene = Scenes::getCurrentScene();
|
||||||
|
|
||||||
|
scene->run();
|
||||||
|
|
||||||
|
// Render fps
|
||||||
|
if (showFps) {
|
||||||
|
++frameCount;
|
||||||
|
auto ticks = timer.getTicks();
|
||||||
|
if (ticks > fpsTick + 50) {
|
||||||
|
fpsTick = ticks;
|
||||||
|
fps = frameCount;
|
||||||
|
frameCount = 0;
|
||||||
|
fpsDigits[0] = '0' + (fps / 10) % 10;
|
||||||
|
fpsDigits[1] = '0' + fps % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
font.renderChar(fpsDigits[0], 0, 0, 242, 0, fb, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
font.renderChar(fpsDigits[1], 7, 0, 242, 0, fb, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
|
||||||
|
video.UpdateRect({0, 0, 14, 9 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for vertical retrace and then copy render buffer to screen
|
||||||
|
video.WaitForVerticalSync();
|
||||||
|
video.Flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayer.stopMusic();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
#include "GameScene.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "../system/Keyboard.h"
|
||||||
|
#include "../system/Timer.h"
|
||||||
|
#include "../system/Video.h"
|
||||||
|
#include "../util/Bmp.h"
|
||||||
|
#include "../util/Gbm.h"
|
||||||
|
#include "../util/Log.h"
|
||||||
|
|
||||||
|
class TestSprite : public Sprite {
|
||||||
|
public:
|
||||||
|
enum { Width = 20, Height = 16, Frames = 12 };
|
||||||
|
|
||||||
|
explicit TestSprite(Bitmap *image) {
|
||||||
|
_bitmap = image;
|
||||||
|
_w = Width;
|
||||||
|
_h = Height;
|
||||||
|
SetX(10 + rand() % (SCREEN_WIDTH - 20 - Width));
|
||||||
|
SetY(10 + rand() % (SCREEN_HEIGHT - 20 - Height));
|
||||||
|
|
||||||
|
int velXSign = ((rand() & 1) << 1) - 1;
|
||||||
|
int velYSign = ((rand() & 1) << 1) - 1;
|
||||||
|
_vx = (rand() % 128 + 64) * velXSign;
|
||||||
|
_vy = (rand() % 128 + 64) * velYSign;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tick() override {
|
||||||
|
if (++_ticks >= 15) {
|
||||||
|
_ticks = 0;
|
||||||
|
if (++_frame >= Frames) {
|
||||||
|
_frame = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newX = _sx + _vx;
|
||||||
|
if (newX < 0) {
|
||||||
|
newX = -newX;
|
||||||
|
_vx = -_vx;
|
||||||
|
} else if (newX >= MaxX) {
|
||||||
|
auto d = newX - MaxX;
|
||||||
|
newX = MaxX - d - 1;
|
||||||
|
_vx = -_vx;
|
||||||
|
}
|
||||||
|
_mirrorX = (_vx < 0);
|
||||||
|
|
||||||
|
auto newY = _sy + _vy;
|
||||||
|
if (newY < 0) {
|
||||||
|
newY = -newY;
|
||||||
|
_vy = -_vy;
|
||||||
|
} else if (newY >= MaxY) {
|
||||||
|
auto d = newY - MaxY;
|
||||||
|
newY = MaxY - d - 1;
|
||||||
|
_vy = -_vy;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sx = newX;
|
||||||
|
_sy = newY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int _vx{0}, _vy{0};
|
||||||
|
unsigned _ticks{0};
|
||||||
|
const int MaxX = (SCREEN_WIDTH - Width) << 8;
|
||||||
|
const int MaxY = (SCREEN_HEIGHT - Height) << 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PlayerSprite : public Sprite {
|
||||||
|
public:
|
||||||
|
enum { Width = 24, Height = 32, Frames = 4, Speed = 480 };
|
||||||
|
|
||||||
|
explicit PlayerSprite(Bitmap *image) {
|
||||||
|
_bitmap = image;
|
||||||
|
_w = Width;
|
||||||
|
_h = Height;
|
||||||
|
SetX(20);
|
||||||
|
SetY((SCREEN_HEIGHT - Height) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tick() override {
|
||||||
|
if (++_ticks >= 15) {
|
||||||
|
_ticks = 0;
|
||||||
|
if (++_animationStep >= Frames) {
|
||||||
|
_animationStep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_frame = _animationMap[_animationStep];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyboard.keyState[KeyLeft]) {
|
||||||
|
_sx -= Speed;
|
||||||
|
if (_sx < 0) _sx = 0;
|
||||||
|
}
|
||||||
|
if (keyboard.keyState[KeyRight]) {
|
||||||
|
_sx += Speed;
|
||||||
|
if (_sx > MaxX) _sx = MaxX;
|
||||||
|
}
|
||||||
|
if (keyboard.keyState[KeyUp]) {
|
||||||
|
_sy -= Speed;
|
||||||
|
if (_sy < 0) _sy = 0;
|
||||||
|
}
|
||||||
|
if (keyboard.keyState[KeyDown]) {
|
||||||
|
_sy += Speed;
|
||||||
|
if (_sy > MaxY) _sy = MaxY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned _ticks{0};
|
||||||
|
unsigned _animationStep = 0;
|
||||||
|
const int MaxX = (SCREEN_WIDTH - Width) << 8;
|
||||||
|
const int MaxY = (SCREEN_HEIGHT - Height) << 8;
|
||||||
|
const unsigned _animationMap[4] = {
|
||||||
|
0, 1, 2, 1
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
GameScene::GameScene() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GameScene::~GameScene() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::run() {
|
||||||
|
if (_firstFrame) {
|
||||||
|
Rect bgRect{0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
|
||||||
|
for (auto y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
|
auto yOffset = y * SCREEN_WIDTH;
|
||||||
|
for (auto x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
|
_fb[yOffset + x] = _bg.GetPixel(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
video.UpdateRect(bgRect);
|
||||||
|
_firstFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraw background
|
||||||
|
for (auto &sprite : _sprites) {
|
||||||
|
clearSprite(sprite);
|
||||||
|
}
|
||||||
|
clearSprite(_playerSprite);
|
||||||
|
|
||||||
|
// Update and paint all sprites after update
|
||||||
|
for (auto &sprite : _sprites) {
|
||||||
|
sprite->Tick();
|
||||||
|
drawSprite(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
_playerSprite->Tick();
|
||||||
|
drawSprite(_playerSprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::enter() {
|
||||||
|
DefaultLog.log("Entering GameScene\n");
|
||||||
|
_fb = static_cast<uint8_t *>(video.GetFB());
|
||||||
|
|
||||||
|
_cow = Gbm::loadFromFile("cow.gbm");
|
||||||
|
_witch = Gbm::loadFromFile("witch.gbm");
|
||||||
|
_bg = Bmp::loadFromFile("bg.bmp");
|
||||||
|
|
||||||
|
_playerSprite = new PlayerSprite(&_witch);
|
||||||
|
_playerSprite->SetPalette(14);
|
||||||
|
|
||||||
|
for (auto &sprite : _sprites) {
|
||||||
|
sprite = new TestSprite(&_cow);
|
||||||
|
sprite->SetPalette(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load bg palette
|
||||||
|
for (auto i = 0u; i < 64; i++) {
|
||||||
|
video.SetPaletteEntry(i, _bg.GetPaletteEntry(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sprite palette
|
||||||
|
for (auto i = 0u; i < 16; i++) {
|
||||||
|
video.SetPaletteEntry(i + 240, _cow.GetPaletteEntry(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load player sprite palette
|
||||||
|
for (auto i = 0u; i < 16; i++) {
|
||||||
|
video.SetPaletteEntry(i + 224, _witch.GetPaletteEntry(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
_firstFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::exit() {
|
||||||
|
DefaultLog.log("Exiting GameScene\n");
|
||||||
|
|
||||||
|
for (auto &sprite : _sprites) {
|
||||||
|
delete sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete _playerSprite;
|
||||||
|
|
||||||
|
_cow.clear();
|
||||||
|
_bg.clear();
|
||||||
|
_witch.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::clearSprite(Sprite *sprite) {
|
||||||
|
auto rect = sprite->GetRect();
|
||||||
|
rect.clamp(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
|
||||||
|
for (auto y = rect.y1; y < rect.y2; y++) {
|
||||||
|
auto yOffset = y * SCREEN_WIDTH;
|
||||||
|
|
||||||
|
for (auto x = rect.x1; x < rect.x2; x++) {
|
||||||
|
_fb[yOffset + x] = _bg.GetPixel(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
video.UpdateRect(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::drawSprite(Sprite *sprite) {
|
||||||
|
auto rect = sprite->GetRect();
|
||||||
|
rect.clamp(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
|
||||||
|
auto rx = rect.x1;
|
||||||
|
auto ry = rect.y1;
|
||||||
|
auto rx2 = rect.x2;
|
||||||
|
auto ry2 = rect.y2;
|
||||||
|
|
||||||
|
auto paletteOffset = sprite->GetPalette() << 4;
|
||||||
|
auto bitmap = sprite->GetBitmap();
|
||||||
|
auto syoff = sprite->GetYOffset();
|
||||||
|
|
||||||
|
if (sprite->isMirroredY()) {
|
||||||
|
for (auto y = ry; y < ry2; y++) {
|
||||||
|
auto yOffset = y * SCREEN_WIDTH;
|
||||||
|
auto offy = ry2 - y - 1;
|
||||||
|
|
||||||
|
if (sprite->isMirroredX()) {
|
||||||
|
for (auto x = rx; x < rx2; x++) {
|
||||||
|
auto offx = rx2 - x - 1;
|
||||||
|
|
||||||
|
auto pv = bitmap->GetPixel(offx, offy + syoff);
|
||||||
|
|
||||||
|
if (!(pv & 0x80)) _fb[yOffset + x] = (pv & 0x7f) + paletteOffset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto x = rx; x < rx2; x++) {
|
||||||
|
auto offx = x - rx;
|
||||||
|
|
||||||
|
auto pv = bitmap->GetPixel(offx, offy + syoff);
|
||||||
|
|
||||||
|
if (!(pv & 0x80)) _fb[yOffset + x] = (pv & 0x7f) + paletteOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto y = ry; y < ry2; y++) {
|
||||||
|
auto yOffset = y * SCREEN_WIDTH;
|
||||||
|
auto offy = y -ry;
|
||||||
|
|
||||||
|
if (sprite->isMirroredX()) {
|
||||||
|
for (auto x = rx; x < rx2; x++) {
|
||||||
|
auto offx = rx2 - x - 1;
|
||||||
|
|
||||||
|
auto pv = bitmap->GetPixel(offx, offy + syoff);
|
||||||
|
|
||||||
|
if (!(pv & 0x80)) _fb[yOffset + x] = (pv & 0x7f) + paletteOffset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto x = rx; x < rx2; x++) {
|
||||||
|
auto offx = x - rx;
|
||||||
|
|
||||||
|
auto pv = bitmap->GetPixel(offx, offy + syoff);
|
||||||
|
|
||||||
|
if (!(pv & 0x80)) _fb[yOffset + x] = (pv & 0x7f) + paletteOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
video.UpdateRect(rect);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef GAME_GAMESCENE_H
|
||||||
|
#define GAME_GAMESCENE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "Scene.h"
|
||||||
|
#include "../graphics/Bitmap.h"
|
||||||
|
#include "../graphics/Sprite.h"
|
||||||
|
|
||||||
|
#define MAX_OBJECTS 16
|
||||||
|
|
||||||
|
class GameScene : public Scene {
|
||||||
|
public:
|
||||||
|
GameScene();
|
||||||
|
~GameScene() override;
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
void enter() override;
|
||||||
|
|
||||||
|
void exit() override;
|
||||||
|
private:
|
||||||
|
uint8_t *_fb{nullptr};
|
||||||
|
|
||||||
|
Bitmap _bg;
|
||||||
|
Bitmap _cow;
|
||||||
|
Bitmap _witch;
|
||||||
|
Sprite *_playerSprite;
|
||||||
|
Sprite *_sprites[MAX_OBJECTS]{nullptr};
|
||||||
|
bool _firstFrame = true;
|
||||||
|
|
||||||
|
void clearSprite(Sprite *sprite);
|
||||||
|
void drawSprite(Sprite *sprite);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_GAMESCENE_H
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "IntroScene.h"
|
||||||
|
|
||||||
|
void IntroScene::run() {
|
||||||
|
Scenes::setScene(Scenes::MainMenuScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntroScene::enter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntroScene::exit() {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef GAME_INTROSCENE_H
|
||||||
|
#define GAME_INTROSCENE_H
|
||||||
|
|
||||||
|
#include "Scene.h"
|
||||||
|
|
||||||
|
|
||||||
|
class IntroScene : public Scene {
|
||||||
|
public:
|
||||||
|
IntroScene() = default;
|
||||||
|
~IntroScene() override = default;
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
void enter() override;
|
||||||
|
|
||||||
|
void exit() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_INTROSCENE_H
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "MainMenuScene.h"
|
||||||
|
|
||||||
|
void MainMenuScene::run() {
|
||||||
|
Scenes::setScene(Scenes::GameScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuScene::enter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuScene::exit() {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef GAME_MAINMENUSCENE_H
|
||||||
|
#define GAME_MAINMENUSCENE_H
|
||||||
|
|
||||||
|
#include "Scene.h"
|
||||||
|
|
||||||
|
|
||||||
|
class MainMenuScene : public Scene {
|
||||||
|
public:
|
||||||
|
MainMenuScene() = default;
|
||||||
|
~MainMenuScene() override = default;
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
void enter() override;
|
||||||
|
|
||||||
|
void exit() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_MAINMENUSCENE_H
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "Scene.h"
|
||||||
|
|
||||||
|
#include "GameScene.h"
|
||||||
|
#include "IntroScene.h"
|
||||||
|
#include "MainMenuScene.h"
|
||||||
|
|
||||||
|
namespace Scenes {
|
||||||
|
static ::IntroScene introScene{};
|
||||||
|
static ::MainMenuScene mainMenuScene{};
|
||||||
|
static ::GameScene gameScene{};
|
||||||
|
|
||||||
|
SceneId currentSceneId = IntroScene;
|
||||||
|
Scene *sceneList[] = {
|
||||||
|
&introScene,
|
||||||
|
&mainMenuScene,
|
||||||
|
&gameScene
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef GAME_SCENE_H
|
||||||
|
#define GAME_SCENE_H
|
||||||
|
|
||||||
|
#include "../graphics/Rect.h"
|
||||||
|
|
||||||
|
class Scene {
|
||||||
|
public:
|
||||||
|
virtual ~Scene() = default;
|
||||||
|
virtual void run() = 0;
|
||||||
|
virtual void enter() = 0;
|
||||||
|
virtual void exit() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Scene() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace Scenes {
|
||||||
|
enum SceneId {
|
||||||
|
IntroScene,
|
||||||
|
MainMenuScene,
|
||||||
|
GameScene,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SceneId currentSceneId;
|
||||||
|
extern Scene *sceneList[];
|
||||||
|
|
||||||
|
inline SceneId setScene(SceneId scene) {
|
||||||
|
auto previousSceneId = currentSceneId;
|
||||||
|
sceneList[currentSceneId]->exit();
|
||||||
|
currentSceneId = scene;
|
||||||
|
sceneList[currentSceneId]->enter();
|
||||||
|
return previousSceneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Scene *getCurrentScene() { return sceneList[currentSceneId]; }
|
||||||
|
inline SceneId getCurrentSceneId() { return currentSceneId; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_SCENE_H
|
||||||
|
|
@ -0,0 +1,429 @@
|
||||||
|
#include "Keyboard.h"
|
||||||
|
|
||||||
|
#include <go32.h>
|
||||||
|
|
||||||
|
#include "Pic.h"
|
||||||
|
|
||||||
|
#define PIC
|
||||||
|
#define KEYB_DATA 0x60
|
||||||
|
|
||||||
|
Keyboard keyboard;
|
||||||
|
|
||||||
|
static const char *keyNames[] = {
|
||||||
|
"<unknown:00>",
|
||||||
|
"KeyEscape", // = 0x01,
|
||||||
|
"Key1", // = 0x02,
|
||||||
|
"Key2", // = 0x03,
|
||||||
|
"Key3", // = 0x04,
|
||||||
|
"Key4", // = 0x05,
|
||||||
|
"Key5", // = 0x06,
|
||||||
|
"Key6", // = 0x07,
|
||||||
|
"Key7", // = 0x08,
|
||||||
|
"Key8", // = 0x09,
|
||||||
|
"Key9", // = 0x0a,
|
||||||
|
"Key0", // = 0x0b,
|
||||||
|
"KeyMinus", // = 0x0c,
|
||||||
|
"KeyEqual", // = 0x0d,
|
||||||
|
"KeyBackspace", // = 0x0e,
|
||||||
|
"KeyTab", // = 0x0f,
|
||||||
|
|
||||||
|
"KeyQ", // = 0x10,
|
||||||
|
"KeyW", // = 0x11,
|
||||||
|
"KeyE", // = 0x12,
|
||||||
|
"KeyR", // = 0x13,
|
||||||
|
"KeyT", // = 0x14,
|
||||||
|
"KeyY", // = 0x15,
|
||||||
|
"KeyU", // = 0x16,
|
||||||
|
"KeyI", // = 0x17,
|
||||||
|
"KeyO", // = 0x18,
|
||||||
|
"KeyP", // = 0x19,
|
||||||
|
"KeyBracketLeft", // = 0x1a,
|
||||||
|
"KeyBracketRight", // = 0x1b,
|
||||||
|
"KeyEnter", // = 0x1c,
|
||||||
|
"KeyLeftCtrl", // = 0x1d,
|
||||||
|
"KeyA", // = 0x1e,
|
||||||
|
"KeyS", // = 0x1f,
|
||||||
|
|
||||||
|
"KeyD", // = 0x20,
|
||||||
|
"KeyF", // = 0x21,
|
||||||
|
"KeyG", // = 0x22,
|
||||||
|
"KeyH", // = 0x23,
|
||||||
|
"KeyJ", // = 0x24,
|
||||||
|
"KeyK", // = 0x25,
|
||||||
|
"KeyL", // = 0x26,
|
||||||
|
"KeySemicolon", // = 0x27,
|
||||||
|
"KeyApostrophe", // = 0x28,
|
||||||
|
"KeyBacktick", // = 0x29,
|
||||||
|
"KeyLeftShift", // = 0x2a,
|
||||||
|
"KeyBackslash", // = 0x2b,
|
||||||
|
"KeyZ", // = 0x2c,
|
||||||
|
"KeyX", // = 0x2d,
|
||||||
|
"KeyC", // = 0x2e,
|
||||||
|
"KeyV", // = 0x2f,
|
||||||
|
|
||||||
|
"KeyB", // = 0x30,
|
||||||
|
"KeyN", // = 0x31,
|
||||||
|
"KeyM", // = 0x32,
|
||||||
|
"KeyComma", // = 0x33,
|
||||||
|
"KeyPeriod", // = 0x34,
|
||||||
|
"KeySlash", // = 0x35,
|
||||||
|
"KeyRightShift", // = 0x36,
|
||||||
|
"KeyKeypadMultiply", // = 0x37,
|
||||||
|
"KeyLeftAlt", // = 0x38,
|
||||||
|
"KeySpace", // = 0x39,
|
||||||
|
"KeyCapsLock", // = 0x3a,
|
||||||
|
"KeyF1", // = 0x3b,
|
||||||
|
"KeyF2", // = 0x3c,
|
||||||
|
"KeyF3", // = 0x3d,
|
||||||
|
"KeyF4", // = 0x3e,
|
||||||
|
"KeyF5", // = 0x3f,
|
||||||
|
|
||||||
|
"KeyF6", // = 0x40,
|
||||||
|
"KeyF7", // = 0x41,
|
||||||
|
"KeyF8", // = 0x42,
|
||||||
|
"KeyF9", // = 0x43,
|
||||||
|
"KeyF10", // = 0x44,
|
||||||
|
"KeyNumLock", // = 0x45,
|
||||||
|
"KeyScrollLock", // = 0x46,
|
||||||
|
"KeyKeypad7", // = 0x47,
|
||||||
|
"KeyKeypad8", // = 0x48,
|
||||||
|
"KeyKeypad9", // = 0x49,
|
||||||
|
"KeyKeypadMinus", // = 0x4a,
|
||||||
|
"KeyKeypad4", // = 0x4b,
|
||||||
|
"KeyKeypad5", // = 0x4c,
|
||||||
|
"KeyKeypad6", // = 0x4d,
|
||||||
|
"KeyKeypadPlus", // = 0x4e,
|
||||||
|
"KeyKeypad1", // = 0x4f,
|
||||||
|
|
||||||
|
"KeyKeypad2", // = 0x50,
|
||||||
|
"KeyKeypad3", // = 0x51,
|
||||||
|
"KeyKeypad0", // = 0x52,
|
||||||
|
"KeyKeypadPeriod", // = 0x53,
|
||||||
|
"<unknown:54>",
|
||||||
|
"<unknown:55>",
|
||||||
|
"<unknown:56>",
|
||||||
|
"KeyF11", // = 0x57,
|
||||||
|
"KeyF12", // = 0x58,
|
||||||
|
"<unknown:59>",
|
||||||
|
"<unknown:5a>",
|
||||||
|
"<unknown:5b>",
|
||||||
|
"<unknown:5c>",
|
||||||
|
"<unknown:5d>",
|
||||||
|
"<unknown:5e>",
|
||||||
|
"<unknown:5f>",
|
||||||
|
|
||||||
|
"<unknown:60>",
|
||||||
|
"<unknown:61>",
|
||||||
|
"<unknown:62>",
|
||||||
|
"<unknown:63>",
|
||||||
|
"<unknown:64>",
|
||||||
|
"<unknown:65>",
|
||||||
|
"<unknown:66>",
|
||||||
|
"<unknown:67>",
|
||||||
|
"<unknown:68>",
|
||||||
|
"<unknown:69>",
|
||||||
|
"<unknown:6a>",
|
||||||
|
"<unknown:6b>",
|
||||||
|
"<unknown:6c>",
|
||||||
|
"<unknown:6d>",
|
||||||
|
"<unknown:6e>",
|
||||||
|
"<unknown:6f>",
|
||||||
|
|
||||||
|
"<unknown:70>",
|
||||||
|
"<unknown:71>",
|
||||||
|
"<unknown:72>",
|
||||||
|
"<unknown:73>",
|
||||||
|
"<unknown:74>",
|
||||||
|
"<unknown:75>",
|
||||||
|
"<unknown:76>",
|
||||||
|
"<unknown:77>",
|
||||||
|
"<unknown:78>",
|
||||||
|
"<unknown:79>",
|
||||||
|
"<unknown:7a>",
|
||||||
|
"<unknown:7b>",
|
||||||
|
"<unknown:7c>",
|
||||||
|
"<unknown:7d>",
|
||||||
|
"<unknown:7e>",
|
||||||
|
"<unknown:7f>",
|
||||||
|
|
||||||
|
"<unknown:80>",
|
||||||
|
"<unknown:81>",
|
||||||
|
"<unknown:82>",
|
||||||
|
"<unknown:83>",
|
||||||
|
"<unknown:84>",
|
||||||
|
"<unknown:85>",
|
||||||
|
"<unknown:86>",
|
||||||
|
"<unknown:87>",
|
||||||
|
"<unknown:88>",
|
||||||
|
"<unknown:89>",
|
||||||
|
"<unknown:8a>",
|
||||||
|
"<unknown:8b>",
|
||||||
|
"<unknown:8c>",
|
||||||
|
"<unknown:8d>",
|
||||||
|
"<unknown:8e>",
|
||||||
|
"<unknown:8f>",
|
||||||
|
|
||||||
|
"KeyMediaPrev", // = 0x90,
|
||||||
|
"<unknown:91>",
|
||||||
|
"<unknown:92>",
|
||||||
|
"<unknown:93>",
|
||||||
|
"<unknown:94>",
|
||||||
|
"<unknown:95>",
|
||||||
|
"<unknown:96>",
|
||||||
|
"<unknown:97>",
|
||||||
|
"<unknown:98>",
|
||||||
|
"KeyMediaNext", // = 0x99,
|
||||||
|
"<unknown:9a>",
|
||||||
|
"<unknown:9b>",
|
||||||
|
"KeyKeypadEnter", // = 0x9c,
|
||||||
|
"KeyRightControl", // = 0x9d,
|
||||||
|
"<unknown:9e>",
|
||||||
|
"<unknown:9f>",
|
||||||
|
|
||||||
|
"KeyMediaMute", // = 0xa0,
|
||||||
|
"KeyMediaCalculator", // = 0xa1,
|
||||||
|
"KeyMediaPlay", // = 0xa2,
|
||||||
|
"<unknown:a3>",
|
||||||
|
"KeyMediaStop", // = 0xa4,
|
||||||
|
"<unknown:a5>",
|
||||||
|
"<unknown:a6>",
|
||||||
|
"<unknown:a7>",
|
||||||
|
"<unknown:a8>",
|
||||||
|
"<unknown:a9>",
|
||||||
|
"<unknown:aa>",
|
||||||
|
"<unknown:ab>",
|
||||||
|
"<unknown:ac>",
|
||||||
|
"<unknown:ad>",
|
||||||
|
"KeyMediaVolumeDown", // = 0xae,
|
||||||
|
"<unknown:af>",
|
||||||
|
|
||||||
|
"KeyMediaVolumeUp", // = 0xb0,
|
||||||
|
"<unknown:b1>",
|
||||||
|
"KeyMediaWww", // = 0xb2,
|
||||||
|
"<unknown:b3>",
|
||||||
|
"<unknown:b4>",
|
||||||
|
"KeyKeypadDivide", // = 0xb5,
|
||||||
|
"<unknown:b6>",
|
||||||
|
"<unknown:b7>",
|
||||||
|
"KeyRightAlt", // = 0xb8,
|
||||||
|
"<unknown:b9>",
|
||||||
|
"<unknown:ba>",
|
||||||
|
"<unknown:bb>",
|
||||||
|
"<unknown:bc>",
|
||||||
|
"<unknown:bd>",
|
||||||
|
"<unknown:be>",
|
||||||
|
"<unknown:bf>",
|
||||||
|
|
||||||
|
"<unknown:c0>",
|
||||||
|
"<unknown:c1>",
|
||||||
|
"<unknown:c2>",
|
||||||
|
"<unknown:c3>",
|
||||||
|
"<unknown:c4>",
|
||||||
|
"<unknown:c5>",
|
||||||
|
"<unknown:c6>",
|
||||||
|
"KeyHome", // = 0xc7,
|
||||||
|
"KeyUp", // = 0xc8,
|
||||||
|
"KeyPageUp", // = 0xc9,
|
||||||
|
"<unknown:ca>",
|
||||||
|
"KeyLeft", // = 0xcb,
|
||||||
|
"<unknown:cc>",
|
||||||
|
"KeyRight", // = 0xcd,
|
||||||
|
"<unknown:ce>",
|
||||||
|
"KeyEnd", // = 0xcf,
|
||||||
|
|
||||||
|
"KeyDown", // = 0xd0,
|
||||||
|
"KeyPageDown", // = 0xd1,
|
||||||
|
"KeyInsert", // = 0xd2,
|
||||||
|
"KeyDelete", // = 0xd3,
|
||||||
|
"<unknown:d4>",
|
||||||
|
"<unknown:d5>",
|
||||||
|
"<unknown:d6>",
|
||||||
|
"<unknown:d7>",
|
||||||
|
"<unknown:d8>",
|
||||||
|
"<unknown:d9>",
|
||||||
|
"<unknown:da>",
|
||||||
|
"KeyLeftGui", // = 0xdb,
|
||||||
|
"KeyRightGui", // = 0xdc,
|
||||||
|
"KeyApps", // = 0xdd,
|
||||||
|
"KeyAcpiPower", // = 0xde,
|
||||||
|
"KeyAcpiSleep", // = 0xdf,
|
||||||
|
|
||||||
|
"<unknown:e0>",
|
||||||
|
"<unknown:e1>",
|
||||||
|
"<unknown:e2>",
|
||||||
|
"KeyAcpiWake", // = 0xe3,
|
||||||
|
"<unknown:e4>",
|
||||||
|
"KeyMediaWwwSearch", // = 0xe5,
|
||||||
|
"KeyMediaWwwFavorites", // = 0xe6,
|
||||||
|
"KeyMediaWwwRefresh", // = 0xe7,
|
||||||
|
"KeyMediaWwwStop", // = 0xe8,
|
||||||
|
"KeyMediaWwwForward", // = 0xe9,
|
||||||
|
"KeyMediaWwwBack", // = 0xea,
|
||||||
|
"KeyMediaMyComputer", // = 0xeb,
|
||||||
|
"KeyMediaEmail", // = 0xec,
|
||||||
|
"KeyMediaSelect", // = 0xed,
|
||||||
|
"<unknown:ee>",
|
||||||
|
"<unknown:ef>",
|
||||||
|
|
||||||
|
"<unknown:f0>",
|
||||||
|
"<unknown:f1>",
|
||||||
|
"<unknown:f2>",
|
||||||
|
"<unknown:f3>",
|
||||||
|
"<unknown:f4>",
|
||||||
|
"<unknown:f5>",
|
||||||
|
"<unknown:f6>",
|
||||||
|
"<unknown:f7>",
|
||||||
|
"<unknown:f8>",
|
||||||
|
"<unknown:f9>",
|
||||||
|
"<unknown:fa>",
|
||||||
|
"<unknown:fb>",
|
||||||
|
"<unknown:fc>",
|
||||||
|
"<unknown:fd>",
|
||||||
|
"KeyPrint", // = 0xfe,
|
||||||
|
"KeyPause", // = 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
void keyboardIsr() {
|
||||||
|
keyboard.Isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard::Keyboard() {
|
||||||
|
_go32_dpmi_get_protected_mode_interrupt_vector(9, &_oldIsr);
|
||||||
|
|
||||||
|
_newIsr.pm_offset = (int)keyboardIsr;
|
||||||
|
_newIsr.pm_selector = _go32_my_cs();
|
||||||
|
_go32_dpmi_allocate_iret_wrapper(&_newIsr);
|
||||||
|
_go32_dpmi_set_protected_mode_interrupt_vector(9, &_newIsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard::~Keyboard() {
|
||||||
|
_go32_dpmi_set_protected_mode_interrupt_vector(9, &_oldIsr);
|
||||||
|
_go32_dpmi_free_iret_wrapper(&_newIsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard::KeyHandleFunction Keyboard::setKeyUpHandler(KeyHandleFunction handler) {
|
||||||
|
auto oldHandler = _keyUpHandler;
|
||||||
|
_keyUpHandler = handler;
|
||||||
|
return oldHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard::KeyHandleFunction Keyboard::setKeyDownHandler(KeyHandleFunction handler) {
|
||||||
|
auto oldHandler = _keyDownHandler;
|
||||||
|
_keyDownHandler = handler;
|
||||||
|
return oldHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard::KeyHandleFunction Keyboard::setKeyRepeatHandler(KeyHandleFunction handler) {
|
||||||
|
auto oldHandler = _keyRepeatHandler;
|
||||||
|
_keyRepeatHandler = handler;
|
||||||
|
return oldHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Keyboard::getKeyName(unsigned char keyCode) {
|
||||||
|
return keyNames[keyCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keyboard::Isr() {
|
||||||
|
auto code = inb(KEYB_DATA);
|
||||||
|
switch (_state) {
|
||||||
|
case Start:
|
||||||
|
if (code == 0xe0) {
|
||||||
|
_state = Extended;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code == 0xe1) {
|
||||||
|
_state = PauseBegin;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code & 0x80) {
|
||||||
|
_state = Start;
|
||||||
|
keyUp(code & 0x7f);
|
||||||
|
} else {
|
||||||
|
_state = Start;
|
||||||
|
keyDown(code & 0x7f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Extended:
|
||||||
|
if (code == 0x2a) {
|
||||||
|
_state = PrintPressed1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code == 0xb7) {
|
||||||
|
_state = PrintReleased1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code & 0x80) {
|
||||||
|
_state = Start;
|
||||||
|
keyUp(0x80 + (code & 0x7f));
|
||||||
|
} else {
|
||||||
|
_state = Start;
|
||||||
|
keyDown(0x80 + code);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PauseBegin:
|
||||||
|
if (code == 0x1d) {
|
||||||
|
_state = PausePressed1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code == 0x9d) {
|
||||||
|
_state = PauseReleased1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
case PausePressed1:
|
||||||
|
if (code == 45) {
|
||||||
|
keyDown(KeyPause);
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
case PauseReleased1:
|
||||||
|
if (code == 0xc5) {
|
||||||
|
keyUp(KeyPause);
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
case PrintPressed1:
|
||||||
|
if (code == 0xe0) {
|
||||||
|
_state = PrintPressed2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
case PrintPressed2:
|
||||||
|
if (code == 0x37) {
|
||||||
|
keyDown(KeyPrint);
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
case PrintReleased1:
|
||||||
|
if (code == 0xe0) {
|
||||||
|
_state = PrintReleased2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
case PrintReleased2:
|
||||||
|
if (code == 0xaa) {
|
||||||
|
keyUp(KeyPrint);
|
||||||
|
}
|
||||||
|
_state = Start;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pic::clearInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keyboard::keyDown(unsigned char c) {
|
||||||
|
if (keyState[c]) {
|
||||||
|
if (_keyRepeatHandler) _keyRepeatHandler(c);
|
||||||
|
} else {
|
||||||
|
keyState[c] = true;
|
||||||
|
if (_keyDownHandler) _keyDownHandler(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keyboard::keyUp(unsigned char c) {
|
||||||
|
keyState[c] = false;
|
||||||
|
if (_keyUpHandler) _keyUpHandler(c);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
#ifndef GAME_KEYBOARD_H
|
||||||
|
#define GAME_KEYBOARD_H
|
||||||
|
|
||||||
|
#include <dpmi.h>
|
||||||
|
|
||||||
|
#define KEY_MAX 256
|
||||||
|
|
||||||
|
enum {
|
||||||
|
KeyEscape = 0x01,
|
||||||
|
Key1 = 0x02,
|
||||||
|
Key2 = 0x03,
|
||||||
|
Key3 = 0x04,
|
||||||
|
Key4 = 0x05,
|
||||||
|
Key5 = 0x06,
|
||||||
|
Key6 = 0x07,
|
||||||
|
Key7 = 0x08,
|
||||||
|
Key8 = 0x09,
|
||||||
|
Key9 = 0x0a,
|
||||||
|
Key0 = 0x0b,
|
||||||
|
KeyMinus = 0x0c,
|
||||||
|
KeyEqual = 0x0d,
|
||||||
|
KeyBackspace = 0x0e,
|
||||||
|
KeyTab = 0x0f,
|
||||||
|
KeyQ = 0x10,
|
||||||
|
KeyW = 0x11,
|
||||||
|
KeyE = 0x12,
|
||||||
|
KeyR = 0x13,
|
||||||
|
KeyT = 0x14,
|
||||||
|
KeyY = 0x15,
|
||||||
|
KeyU = 0x16,
|
||||||
|
KeyI = 0x17,
|
||||||
|
KeyO = 0x18,
|
||||||
|
KeyP = 0x19,
|
||||||
|
KeyBracketLeft = 0x1a,
|
||||||
|
KeyBracketRight = 0x1b,
|
||||||
|
KeyEnter = 0x1c,
|
||||||
|
KeyLeftCtrl = 0x1d,
|
||||||
|
KeyA = 0x1e,
|
||||||
|
KeyS = 0x1f,
|
||||||
|
KeyD = 0x20,
|
||||||
|
KeyF = 0x21,
|
||||||
|
KeyG = 0x22,
|
||||||
|
KeyH = 0x23,
|
||||||
|
KeyJ = 0x24,
|
||||||
|
KeyK = 0x25,
|
||||||
|
KeyL = 0x26,
|
||||||
|
KeySemicolon = 0x27,
|
||||||
|
KeyApostrophe = 0x28,
|
||||||
|
KeyBacktick = 0x29,
|
||||||
|
KeyLeftShift = 0x2a,
|
||||||
|
KeyBackslash = 0x2b,
|
||||||
|
KeyZ = 0x2c,
|
||||||
|
KeyX = 0x2d,
|
||||||
|
KeyC = 0x2e,
|
||||||
|
KeyV = 0x2f,
|
||||||
|
KeyB = 0x30,
|
||||||
|
KeyN = 0x31,
|
||||||
|
KeyM = 0x32,
|
||||||
|
KeyComma = 0x33,
|
||||||
|
KeyPeriod = 0x34,
|
||||||
|
KeySlash = 0x35,
|
||||||
|
KeyRightShift = 0x36,
|
||||||
|
KeyKeypadMultiply = 0x37,
|
||||||
|
KeyLeftAlt = 0x38,
|
||||||
|
KeySpace = 0x39,
|
||||||
|
KeyCapsLock = 0x3a,
|
||||||
|
KeyF1 = 0x3b,
|
||||||
|
KeyF2 = 0x3c,
|
||||||
|
KeyF3 = 0x3d,
|
||||||
|
KeyF4 = 0x3e,
|
||||||
|
KeyF5 = 0x3f,
|
||||||
|
KeyF6 = 0x40,
|
||||||
|
KeyF7 = 0x41,
|
||||||
|
KeyF8 = 0x42,
|
||||||
|
KeyF9 = 0x43,
|
||||||
|
KeyF10 = 0x44,
|
||||||
|
KeyNumLock = 0x45,
|
||||||
|
KeyScrollLock = 0x46,
|
||||||
|
KeyKeypad7 = 0x47,
|
||||||
|
KeyKeypad8 = 0x48,
|
||||||
|
KeyKeypad9 = 0x49,
|
||||||
|
KeyKeypadMinus = 0x4a,
|
||||||
|
KeyKeypad4 = 0x4b,
|
||||||
|
KeyKeypad5 = 0x4c,
|
||||||
|
KeyKeypad6 = 0x4d,
|
||||||
|
KeyKeypadPlus = 0x4e,
|
||||||
|
KeyKeypad1 = 0x4f,
|
||||||
|
KeyKeypad2 = 0x50,
|
||||||
|
KeyKeypad3 = 0x51,
|
||||||
|
KeyKeypad0 = 0x52,
|
||||||
|
KeyKeypadPeriod = 0x53,
|
||||||
|
KeyF11 = 0x57,
|
||||||
|
KeyF12 = 0x58,
|
||||||
|
KeyMediaPrev = 0x90,
|
||||||
|
KeyMediaNext = 0x99,
|
||||||
|
KeyKeypadEnter = 0x9c,
|
||||||
|
KeyRightControl = 0x9d,
|
||||||
|
KeyMediaMute = 0xa0,
|
||||||
|
KeyMediaCalculator = 0xa1,
|
||||||
|
KeyMediaPlay = 0xa2,
|
||||||
|
KeyMediaStop = 0xa4,
|
||||||
|
KeyMediaVolumeDown = 0xae,
|
||||||
|
KeyMediaVolumeUp = 0xb0,
|
||||||
|
KeyMediaWww = 0xb2,
|
||||||
|
KeyKeypadDivide = 0xb5,
|
||||||
|
KeyRightAlt = 0xb8,
|
||||||
|
KeyHome = 0xc7,
|
||||||
|
KeyUp = 0xc8,
|
||||||
|
KeyPageUp = 0xc9,
|
||||||
|
KeyLeft = 0xcb,
|
||||||
|
KeyRight = 0xcd,
|
||||||
|
KeyEnd = 0xcf,
|
||||||
|
KeyDown = 0xd0,
|
||||||
|
KeyPageDown = 0xd1,
|
||||||
|
KeyInsert = 0xd2,
|
||||||
|
KeyDelete = 0xd3,
|
||||||
|
KeyLeftGui = 0xdb,
|
||||||
|
KeyRightGui = 0xdc,
|
||||||
|
KeyApps = 0xdd,
|
||||||
|
KeyAcpiPower = 0xde,
|
||||||
|
KeyAcpiSleep = 0xdf,
|
||||||
|
KeyAcpiWake = 0xe3,
|
||||||
|
KeyMediaWwwSearch = 0xe5,
|
||||||
|
KeyMediaWwwFavorites = 0xe6,
|
||||||
|
KeyMediaWwwRefresh = 0xe7,
|
||||||
|
KeyMediaWwwStop = 0xe8,
|
||||||
|
KeyMediaWwwForward = 0xe9,
|
||||||
|
KeyMediaWwwBack = 0xea,
|
||||||
|
KeyMediaMyComputer = 0xeb,
|
||||||
|
KeyMediaEmail = 0xec,
|
||||||
|
KeyMediaSelect = 0xed,
|
||||||
|
KeyPrint = 0xfe,
|
||||||
|
KeyPause = 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Keyboard {
|
||||||
|
public:
|
||||||
|
using KeyHandleFunction = void (*)(unsigned char);
|
||||||
|
|
||||||
|
Keyboard();
|
||||||
|
~Keyboard();
|
||||||
|
|
||||||
|
KeyHandleFunction setKeyUpHandler(KeyHandleFunction handler);
|
||||||
|
KeyHandleFunction setKeyDownHandler(KeyHandleFunction handler);
|
||||||
|
KeyHandleFunction setKeyRepeatHandler(KeyHandleFunction handler);
|
||||||
|
|
||||||
|
bool keyState[KEY_MAX]{};
|
||||||
|
|
||||||
|
const char *getKeyName(unsigned char keyCode);
|
||||||
|
private:
|
||||||
|
friend void keyboardIsr();
|
||||||
|
void Isr();
|
||||||
|
enum State {
|
||||||
|
Start,
|
||||||
|
Extended,
|
||||||
|
PauseBegin,
|
||||||
|
PausePressed1,
|
||||||
|
PauseReleased1,
|
||||||
|
PrintPressed1,
|
||||||
|
PrintPressed2,
|
||||||
|
PrintReleased1,
|
||||||
|
PrintReleased2,
|
||||||
|
};
|
||||||
|
State _state;
|
||||||
|
KeyHandleFunction _keyDownHandler{nullptr}, _keyUpHandler{nullptr}, _keyRepeatHandler{nullptr};
|
||||||
|
|
||||||
|
void keyDown(unsigned char c);
|
||||||
|
|
||||||
|
void keyUp(unsigned char c);
|
||||||
|
|
||||||
|
_go32_dpmi_seginfo _oldIsr, _newIsr;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Keyboard keyboard;
|
||||||
|
|
||||||
|
#endif //GAME_KEYBOARD_H
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef GAME_PIC_H
|
||||||
|
#define GAME_PIC_H
|
||||||
|
|
||||||
|
#include "../util/Asm.h"
|
||||||
|
|
||||||
|
namespace Pic {
|
||||||
|
static inline void clearInterrupt() {
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //GAME_PIC_H
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include <go32.h>
|
||||||
|
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
|
#include "Pic.h"
|
||||||
|
#include "../util/Asm.h"
|
||||||
|
|
||||||
|
#define PIT_FREQ 1193181
|
||||||
|
#define PIT_MODE_CH0 0x00
|
||||||
|
#define PIT_MODE_CH1 0x40
|
||||||
|
#define PIT_MODE_CH2 0x80
|
||||||
|
#define PIT_MODE_ACCESS_LO 0x10
|
||||||
|
#define PIT_MODE_ACCESS_HI 0x20
|
||||||
|
#define PIT_MODE_ACCESS_LOHI 0x30
|
||||||
|
#define PIT_MODE_M0 0x00
|
||||||
|
#define PIT_MODE_M1 0x02
|
||||||
|
#define PIT_MODE_M2 0x04
|
||||||
|
#define PIT_MODE_M3 0x06
|
||||||
|
#define PIT_MODE_M4 0x08
|
||||||
|
#define PIT_MODE_M5 0x0a
|
||||||
|
#define PIT_MODE_BIN 0x00
|
||||||
|
#define PIT_MODE_BCD 0x01
|
||||||
|
|
||||||
|
#define PIT_REG_CH0 0x40
|
||||||
|
#define PIT_REG_CH1 0x41
|
||||||
|
#define PIT_REG_CH2 0x42
|
||||||
|
#define PIT_REG_MODE 0x43
|
||||||
|
|
||||||
|
Timer timer;
|
||||||
|
|
||||||
|
void timerISR() {
|
||||||
|
timer.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::Timer() {
|
||||||
|
_go32_dpmi_get_protected_mode_interrupt_vector(8, &_oldIsr);
|
||||||
|
|
||||||
|
_newIsr.pm_offset = reinterpret_cast<int>(timerISR);
|
||||||
|
_newIsr.pm_selector = _go32_my_cs();
|
||||||
|
_go32_dpmi_allocate_iret_wrapper(&_newIsr);
|
||||||
|
_go32_dpmi_set_protected_mode_interrupt_vector(8, &_newIsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::~Timer() {
|
||||||
|
setDivider(65535);
|
||||||
|
_go32_dpmi_set_protected_mode_interrupt_vector(8, &_oldIsr);
|
||||||
|
_go32_dpmi_free_iret_wrapper(&_newIsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::update() {
|
||||||
|
_ticks++;
|
||||||
|
|
||||||
|
if (_callback) _callback();
|
||||||
|
|
||||||
|
_elapsed += _div;
|
||||||
|
if (_elapsed >= 65535) {
|
||||||
|
_elapsed -= 65535;
|
||||||
|
// TODO call old isr
|
||||||
|
// Documentation is kind of nonexistant so we will not call the handler for now
|
||||||
|
Pic::clearInterrupt();
|
||||||
|
} else {
|
||||||
|
Pic::clearInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Timer::getTicks() const {
|
||||||
|
return _ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::setFrequency(uint16_t freq) {
|
||||||
|
setDivider(PIT_FREQ / freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::setDivider(uint16_t div) {
|
||||||
|
auto wasEnabled = interruptsEnabled();
|
||||||
|
disableInterrupts();
|
||||||
|
|
||||||
|
_div = div;
|
||||||
|
|
||||||
|
// Update PIT frequency
|
||||||
|
outb(PIT_REG_MODE, PIT_MODE_CH0|PIT_MODE_ACCESS_LOHI|PIT_MODE_M2|PIT_MODE_BIN);
|
||||||
|
outb(PIT_REG_CH0, _div & 0xff);
|
||||||
|
outb(PIT_REG_CH0, (_div >> 8) & 0xff);
|
||||||
|
|
||||||
|
if (1) enableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::Callback Timer::setCallback(Timer::Callback callback) {
|
||||||
|
auto oldCallback = _callback;
|
||||||
|
_callback = callback;
|
||||||
|
return oldCallback;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef TICKS_H
|
||||||
|
#define TICKS_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <dpmi.h>
|
||||||
|
|
||||||
|
class Timer {
|
||||||
|
public:
|
||||||
|
using Callback = void (*)();
|
||||||
|
|
||||||
|
Timer();
|
||||||
|
|
||||||
|
~Timer();
|
||||||
|
|
||||||
|
void setFrequency(uint16_t freq);
|
||||||
|
|
||||||
|
void setDivider(uint16_t div);
|
||||||
|
|
||||||
|
Callback setCallback(Callback);
|
||||||
|
|
||||||
|
[[nodiscard]] uint32_t getTicks() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend void timerISR();
|
||||||
|
|
||||||
|
_go32_dpmi_seginfo _oldIsr{}, _newIsr{};
|
||||||
|
uint32_t _ticks{0};
|
||||||
|
uint32_t _elapsed{0};
|
||||||
|
uint16_t _div{0};
|
||||||
|
|
||||||
|
void (*_callback)(){nullptr};
|
||||||
|
|
||||||
|
void update();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Timer timer;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
#include <dos.h>
|
||||||
|
#include <sys/nearptr.h>
|
||||||
|
|
||||||
|
#include "Video.h"
|
||||||
|
#include "../util/Asm.h"
|
||||||
|
#include "../util/Log.h"
|
||||||
|
|
||||||
|
#define VGA_DAC_ADDR_RD 0x3c7
|
||||||
|
#define VGA_DAC_ADDR_WR 0x3c8
|
||||||
|
#define VGA_DAC_DATA 0x3c9
|
||||||
|
#define VGA_EXT_INPUT_STATUS 0x3da
|
||||||
|
#define VGA_EXT_INPUT_STATUS_VRETRACE 8
|
||||||
|
|
||||||
|
Video video;
|
||||||
|
|
||||||
|
Video::Video() {
|
||||||
|
__djgpp_nearptr_enable();
|
||||||
|
|
||||||
|
// Store current video mode
|
||||||
|
_oldMode = GetMode();
|
||||||
|
|
||||||
|
// Store current palette
|
||||||
|
for (auto i = 0; i < 256; i++) {
|
||||||
|
GetPaletteEntry(i, &_oldPalette[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Video::~Video() {
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::Enter() {
|
||||||
|
SetMode(0x13);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::Exit() {
|
||||||
|
// Restore old palette
|
||||||
|
for (auto i = 0; i < 256; i++) {
|
||||||
|
SetPaletteEntry(i, &_oldPalette[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore old video mode
|
||||||
|
SetMode(_oldMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::SetMode(uint8_t mode) {
|
||||||
|
union REGS regs;
|
||||||
|
regs.h.ah = 0x00;
|
||||||
|
regs.h.al = mode;
|
||||||
|
int86(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
//__djgpp_nearptr_enable();
|
||||||
|
switch (mode) {
|
||||||
|
default:
|
||||||
|
case 0x13:
|
||||||
|
_fb = (uint8_t *)(0xA0000 + __djgpp_conventional_base);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Video::GetMode() {
|
||||||
|
union REGS regs;
|
||||||
|
regs.h.ah = 0x0f;
|
||||||
|
regs.h.al = 0;
|
||||||
|
int86(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
return regs.h.al;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::SetPaletteEntry(uint8_t index, PaletteEntry entry) {
|
||||||
|
SetPaletteEntry(index, &entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Video::PaletteEntry Video::GetPaletteEntry(uint8_t index) {
|
||||||
|
PaletteEntry entry;
|
||||||
|
GetPaletteEntry(index, &entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::SetPaletteEntry(uint8_t index, PaletteEntry *entry) {
|
||||||
|
outb(VGA_DAC_ADDR_WR, index);
|
||||||
|
outb(VGA_DAC_DATA, entry->r >> 2);
|
||||||
|
outb(VGA_DAC_DATA, entry->g >> 2);
|
||||||
|
outb(VGA_DAC_DATA, entry->b >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::GetPaletteEntry(uint8_t index, PaletteEntry *entry) {
|
||||||
|
outb(VGA_DAC_ADDR_RD, index);
|
||||||
|
entry->r = inb(VGA_DAC_DATA);
|
||||||
|
entry->g = inb(VGA_DAC_DATA);
|
||||||
|
entry->b = inb(VGA_DAC_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Video::GetFB() {
|
||||||
|
return _renderBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::WaitForVerticalSync() {
|
||||||
|
auto state = inb(VGA_EXT_INPUT_STATUS) & VGA_EXT_INPUT_STATUS_VRETRACE;
|
||||||
|
if (!state) {
|
||||||
|
// We started before a retrace, so wait until it begins
|
||||||
|
while (!(inb(VGA_EXT_INPUT_STATUS) & VGA_EXT_INPUT_STATUS_VRETRACE)) {}
|
||||||
|
}
|
||||||
|
// Wait until it ends
|
||||||
|
while (inb(VGA_EXT_INPUT_STATUS) & VGA_EXT_INPUT_STATUS_VRETRACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::UpdateRect(const Rect &rect) {
|
||||||
|
// Merge rect if it overlaps with an existing rect
|
||||||
|
for (auto i = 0; i < _updateRectIndex; i++) {
|
||||||
|
auto &r = _updatedRects[i];
|
||||||
|
if (r.overlaps(rect)) {
|
||||||
|
r += rect;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new rect to the list
|
||||||
|
_updatedRects[_updateRectIndex++] = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::Flip() {
|
||||||
|
for (auto i = 0; i <= MAX_UPDATE_RECT_INDEX; i++) {
|
||||||
|
auto &rect = _updatedRects[i];
|
||||||
|
for (auto y = rect.y1; y <= rect.y2; y++) {
|
||||||
|
auto yOffset = y * SCREEN_WIDTH;
|
||||||
|
|
||||||
|
for (auto x = rect.x1; x <= rect.x2; x++) {
|
||||||
|
_fb[yOffset + x] = _renderBuffer[yOffset + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateRectIndex = 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef VIDEO_H
|
||||||
|
#define VIDEO_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../graphics/Rect.h"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 320
|
||||||
|
#define SCREEN_HEIGHT 200
|
||||||
|
#define MAX_UPDATE_RECT_INDEX 255
|
||||||
|
|
||||||
|
class Video {
|
||||||
|
public:
|
||||||
|
struct PaletteEntry {
|
||||||
|
uint8_t r{0}, g{0}, b{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
Video();
|
||||||
|
~Video();
|
||||||
|
|
||||||
|
void Enter();
|
||||||
|
void Exit();
|
||||||
|
|
||||||
|
void SetPaletteEntry(uint8_t index, PaletteEntry entry);
|
||||||
|
PaletteEntry GetPaletteEntry(uint8_t index);
|
||||||
|
|
||||||
|
void SetPaletteEntry(uint8_t index, PaletteEntry *entry);
|
||||||
|
void GetPaletteEntry(uint8_t index, PaletteEntry *entry);
|
||||||
|
|
||||||
|
void *GetFB();
|
||||||
|
|
||||||
|
void WaitForVerticalSync();
|
||||||
|
|
||||||
|
void UpdateRect(const Rect &rect);
|
||||||
|
void Flip();
|
||||||
|
private:
|
||||||
|
uint8_t _oldMode{0};
|
||||||
|
PaletteEntry _oldPalette[256]{};
|
||||||
|
uint8_t *_fb{nullptr};
|
||||||
|
uint8_t _renderBuffer[SCREEN_WIDTH*SCREEN_HEIGHT]{};
|
||||||
|
Rect _updatedRects[MAX_UPDATE_RECT_INDEX+1];
|
||||||
|
unsigned _updateRectIndex{0};
|
||||||
|
|
||||||
|
void SetMode(uint8_t mode);
|
||||||
|
uint8_t GetMode();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Video video;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#ifndef ASM_H
|
||||||
|
#define ASM_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
static inline uint8_t inb(uint16_t port) {
|
||||||
|
uint8_t value;
|
||||||
|
asm volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t inw(uint16_t port) {
|
||||||
|
uint16_t value;
|
||||||
|
asm volatile ("inw %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t inl(uint16_t port) {
|
||||||
|
uint32_t value;
|
||||||
|
asm volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outb(uint16_t port, uint8_t value) {
|
||||||
|
asm volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outw(uint16_t port, uint16_t value) {
|
||||||
|
asm volatile ("outw %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outl(uint16_t port, uint32_t value) {
|
||||||
|
asm volatile ("outl %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void enableInterrupts() {
|
||||||
|
asm volatile ("sti": : :"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void disableInterrupts() {
|
||||||
|
asm volatile ("cli": : :"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t eflags() {
|
||||||
|
uint32_t value;
|
||||||
|
asm volatile ( "pushf\n\t"
|
||||||
|
"popl %0"
|
||||||
|
: "=g"(value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool interruptsEnabled() {
|
||||||
|
return (eflags() & (1u << 9u)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmInterruptCall(unsigned short selector, unsigned long offset) {
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"pushf\n" // Push EFLAGS
|
||||||
|
"mov %0, %%ax\n" // Load the segment selector into AX
|
||||||
|
"mov %%ax, %%ds\n" // Move it to DS (or another segment register)
|
||||||
|
"call *%1\n" // Perform the indirect far call using the offset
|
||||||
|
: // No output operands
|
||||||
|
: "r" (selector), "r" (offset) // Input operands
|
||||||
|
: "%ax" // Clobbered register
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
#include "Bmp.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "Files.h"
|
||||||
|
|
||||||
|
namespace Bmp {
|
||||||
|
Bitmap loadFromMemory(void *buf, size_t len) {
|
||||||
|
if (len < sizeof (BmpFileHeader) + sizeof (BmpInfoHeader)) return {};
|
||||||
|
|
||||||
|
auto fileHeader = reinterpret_cast<BmpFileHeader *>(buf);
|
||||||
|
auto infoHeader = reinterpret_cast<BmpInfoHeader *>(fileHeader + 1);
|
||||||
|
|
||||||
|
if (fileHeader->signature != 0x4d42) {
|
||||||
|
printf("Invalid signature\n");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (len < fileHeader->fileSize) {
|
||||||
|
printf("File too small\n");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (infoHeader->headerSize != 40) {
|
||||||
|
printf("Invalid header size\n");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (infoHeader->width < 0 || infoHeader->height < 0 || infoHeader->planes != 1) {
|
||||||
|
printf("Invalid width/height/bit planes\n");
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (infoHeader->bitsPerPixel != 1 && infoHeader->bitsPerPixel != 4 && infoHeader->bitsPerPixel != 8) {
|
||||||
|
printf("Unsupported bpp %u\n", infoHeader->bitsPerPixel);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (infoHeader->compression != BmpInfoHeader::CompressionType::Rgb) {
|
||||||
|
printf("Unsupported compression\n");
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check imageSize
|
||||||
|
|
||||||
|
Bitmap bitmap(infoHeader->width, infoHeader->height);
|
||||||
|
|
||||||
|
// Read color table
|
||||||
|
auto colorTable = reinterpret_cast<uint8_t *>(infoHeader + 1);
|
||||||
|
for (auto i = 0; i < infoHeader->colorsUsed; i++) {
|
||||||
|
Video::PaletteEntry paletteEntry;
|
||||||
|
paletteEntry.b = colorTable[4 * i + 0];
|
||||||
|
paletteEntry.g = colorTable[4 * i + 1];
|
||||||
|
paletteEntry.r = colorTable[4 * i + 2];
|
||||||
|
bitmap.SetPaletteEntry(i, paletteEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read pixel data
|
||||||
|
auto data = static_cast<uint8_t *>(buf) + fileHeader->offset;
|
||||||
|
auto rowLength = infoHeader->width;
|
||||||
|
if (infoHeader->bitsPerPixel == 1) rowLength = (rowLength + 7) >> 3;
|
||||||
|
else if (infoHeader->bitsPerPixel == 4) rowLength = (rowLength + 1) >> 1;
|
||||||
|
auto stride = (rowLength + 3) >> 2 << 2;
|
||||||
|
for (auto y = 0u; y < infoHeader->height; y++) {
|
||||||
|
for (auto x = 0u; x < infoHeader->width; x++) {
|
||||||
|
uint8_t pv = 0;
|
||||||
|
switch (infoHeader->bitsPerPixel) {
|
||||||
|
case 1: {
|
||||||
|
const auto byte = data[(infoHeader->height - y - 1) * stride + (x >> 3)];
|
||||||
|
auto pixelOffset = x & 7;
|
||||||
|
pv = (byte >> (7 - pixelOffset)) & 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
const auto byte = data[(infoHeader->height - y - 1) * stride + (x >> 1)];
|
||||||
|
pv = x & 1 ? byte & 0xf : byte >> 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 8: {
|
||||||
|
pv = data[(infoHeader->height - y - 1) * stride + x];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitmap.SetPixel(x, y, pv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap loadFromFile(const char* path) {
|
||||||
|
void *data;
|
||||||
|
auto len = Files::allocateBufferAndLoadFromFile(path, &data);
|
||||||
|
auto bitmap = loadFromMemory(data, len);
|
||||||
|
Files::deleteBuffer(data);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef GAME_BMPLOADER_H
|
||||||
|
#define GAME_BMPLOADER_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include "../graphics/Bitmap.h"
|
||||||
|
|
||||||
|
namespace Bmp {
|
||||||
|
struct BmpFileHeader {
|
||||||
|
uint16_t signature;
|
||||||
|
uint32_t fileSize;
|
||||||
|
uint16_t reserved1;
|
||||||
|
uint16_t reserved2;
|
||||||
|
uint32_t offset;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
struct BmpInfoHeader {
|
||||||
|
uint32_t headerSize;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint16_t planes;
|
||||||
|
uint16_t bitsPerPixel;
|
||||||
|
uint32_t compression;
|
||||||
|
uint32_t imageSize;
|
||||||
|
uint32_t horizontalResolution;
|
||||||
|
uint32_t verticalResolution;
|
||||||
|
uint32_t colorsUsed;
|
||||||
|
uint32_t colorsImportant;
|
||||||
|
|
||||||
|
enum CompressionType {
|
||||||
|
Rgb = 0,
|
||||||
|
Rle8,
|
||||||
|
Rle4,
|
||||||
|
Bitfields,
|
||||||
|
Jpeg,
|
||||||
|
Png,
|
||||||
|
Alphabitfields,
|
||||||
|
Cmyk,
|
||||||
|
CmykRle8,
|
||||||
|
CmykRle4,
|
||||||
|
};
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
Bitmap loadFromMemory(void *buf, size_t len);
|
||||||
|
Bitmap loadFromFile(const char* path);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_BMPLOADER_H
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "Files.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
size_t Files::allocateBufferAndLoadFromFile(const char *path, void **bufferPointer) {
|
||||||
|
auto fp = fopen(path, "rb");
|
||||||
|
if (!fp) {
|
||||||
|
*bufferPointer = nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
size_t len = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
auto data = new unsigned char[len];
|
||||||
|
fread(data, 1, len, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
*bufferPointer = data;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Files::deleteBuffer(void *buffer) {
|
||||||
|
delete[] static_cast<unsigned char*>(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef GAME_FILES_H
|
||||||
|
#define GAME_FILES_H
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace Files {
|
||||||
|
size_t allocateBufferAndLoadFromFile(const char *path, void **bufferPointer);
|
||||||
|
void deleteBuffer(void *);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //GAME_FILES_H
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "Gbm.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "Files.h"
|
||||||
|
|
||||||
|
#define GBM_SIGNATURE 0x204d4247
|
||||||
|
|
||||||
|
Bitmap Gbm::loadFromMemory(const void *data, size_t size) {
|
||||||
|
// Check for minimum
|
||||||
|
if (size < sizeof (uint32_t) + sizeof (uint16_t) + sizeof (uint16_t) + 16 * 3) return {};
|
||||||
|
|
||||||
|
auto gbm = static_cast<const GbmHeader *>(data);
|
||||||
|
|
||||||
|
if (gbm->signature != GBM_SIGNATURE) return {};
|
||||||
|
if (gbm->width == 0 || gbm->height == 0) return {};
|
||||||
|
|
||||||
|
Bitmap bitmap(gbm->width, gbm->height);
|
||||||
|
for (auto i = 0; i < 16; i++) {
|
||||||
|
bitmap.SetPaletteEntry(i, {gbm->palette[i].r, gbm->palette[i].g, gbm->palette[i].b});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto y = 0; y < gbm->height; y++) {
|
||||||
|
for (auto x = 0; x < gbm->width; x++) {
|
||||||
|
auto pv = gbm->data[y * gbm->width + x];
|
||||||
|
bitmap.SetPixel(x, y, pv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap Gbm::loadFromFile(const char *path) {
|
||||||
|
void *data;
|
||||||
|
auto len = Files::allocateBufferAndLoadFromFile(path, &data);
|
||||||
|
auto bitmap = loadFromMemory(data, len);
|
||||||
|
Files::deleteBuffer(data);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gbm::writeToFile(const char *path, const Bitmap &bitmap) {
|
||||||
|
auto fp = fopen(path, "wb");
|
||||||
|
if (!fp) return;
|
||||||
|
|
||||||
|
// Write signature
|
||||||
|
uint32_t signature = GBM_SIGNATURE;
|
||||||
|
fwrite(&signature, 4, 1, fp);
|
||||||
|
uint16_t width = bitmap.GetWidth(), height = bitmap.GetHeight();
|
||||||
|
fwrite(&width, 2, 1, fp);
|
||||||
|
fwrite(&height, 2, 1, fp);
|
||||||
|
for (auto i = 0; i < 16; i++) {
|
||||||
|
auto entry = bitmap.GetPaletteEntry(i);
|
||||||
|
auto r = entry.r, g = entry.g, b = entry.b;
|
||||||
|
fwrite(&r, 1, 1, fp);
|
||||||
|
fwrite(&g, 1, 1, fp);
|
||||||
|
fwrite(&b, 1, 1, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto y = 0; y < height; y++) {
|
||||||
|
for (auto x = 0; x < width; x++) {
|
||||||
|
auto pv = bitmap.GetPixel(x, y);
|
||||||
|
fwrite(&pv, 1, 1, fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef GAME_GBM_H
|
||||||
|
#define GAME_GBM_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "../graphics/Bitmap.h"
|
||||||
|
|
||||||
|
namespace Gbm {
|
||||||
|
struct GbmHeader {
|
||||||
|
uint32_t signature;
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
struct {
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
} __attribute__((__packed__)) palette[16];
|
||||||
|
uint8_t data[0];
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
Bitmap loadFromMemory(const void *data, size_t size);
|
||||||
|
Bitmap loadFromFile(const char *path);
|
||||||
|
|
||||||
|
void writeToFile(const char *path, const Bitmap &bitmap);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_GBM_H
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
|
Log DefaultLog("log.txt");
|
||||||
|
|
||||||
|
Log::Log(const char *path) {
|
||||||
|
fp = fopen(path, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::~Log() {
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log::log(const char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(fp, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
fflush(fp);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef GAME_LOG_H
|
||||||
|
#define GAME_LOG_H
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
class Log {
|
||||||
|
public:
|
||||||
|
Log(const char *path);
|
||||||
|
~Log();
|
||||||
|
|
||||||
|
void log(const char *fmt, ...);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *fp;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Log DefaultLog;
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GAME_LOG_H
|
||||||