From b1d1573702f21e3104606d6ade5f5d39ec86a33a Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Thu, 23 Jan 2025 22:02:11 +0100 Subject: restructure project --- app/mac/scene.mm | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 app/mac/scene.mm (limited to 'app/mac/scene.mm') diff --git a/app/mac/scene.mm b/app/mac/scene.mm new file mode 100644 index 0000000..f858185 --- /dev/null +++ b/app/mac/scene.mm @@ -0,0 +1,179 @@ +#import "scene.hh" +#import "nes/nes.hh" +#import "nes/display.hh" +#import "nes/util/rgb.hh" +#import +#import + +namespace +{ + class display_impl : public nes::display + { + SKSpriteNode* node_; + NSMutableData* buffer_front_; + NSMutableData* buffer_back_; + std::uint8_t* bytes_back_; + + public: + explicit display_impl(SKSpriteNode* node) + : node_{ node } + , buffer_front_{ make_buffer() } + , buffer_back_{ make_buffer() } + , bytes_back_{ static_cast([buffer_back_ mutableBytes]) } + { + } + + ~display_impl() = default; + + virtual auto switch_buffers() -> void override + { + auto tmp = buffer_back_; + buffer_back_ = buffer_front_; + buffer_front_ = tmp; + bytes_back_ = static_cast([buffer_back_ mutableBytes]); + } + + auto actually_switch_buffers() -> void + { + auto texture = [SKTexture textureWithData:buffer_front_ size:CGSizeMake(width, height) flipped:YES]; + [texture setFilteringMode:SKTextureFilteringNearest]; + [node_ setTexture:texture]; + } + + virtual auto set(unsigned x, unsigned y, nes::rgb value) -> void override + { + auto const offset = (y * width + x) * 4; + bytes_back_[offset + 0] = value.r; + bytes_back_[offset + 1] = value.g; + bytes_back_[offset + 2] = value.b; + bytes_back_[offset + 3] = 255; + } + + virtual auto get(unsigned x, unsigned y) const -> nes::rgb override + { + auto const offset = (y * width + x) * 4; + auto res = nes::rgb{}; + res.r = bytes_back_[offset + 0]; + res.g = bytes_back_[offset + 1]; + res.b = bytes_back_[offset + 2]; + return res; + } + + private: + static auto make_buffer() -> NSMutableData* + { + auto const length = NSUInteger{ width * height * 4 }; + return [NSMutableData dataWithLength:length]; + } + }; +} // namespace + +@implementation Scene +{ + std::unique_ptr display_; + std::unique_ptr nes_; + std::optional last_time_; +} + +- (instancetype)initWithFilePath:(NSString*)filePath +{ + self = [super initWithSize:CGSizeMake(nes::display::width, nes::display::height)]; + if (self) + { + auto node = [[SKSpriteNode alloc] init]; + [node setAnchorPoint:CGPointZero]; + [node setSize:[self size]]; + + [self setScaleMode:SKSceneScaleModeAspectFit]; + [self addChild:node]; + + auto cartridge = nes::cartridge::from_file([filePath UTF8String]); + + self->display_ = std::make_unique(node); + self->nes_ = std::make_unique(std::move(cartridge), *self->display_); + } + return self; +} + +- (void)keyDown:(NSEvent*)event +{ + auto& controller_1 = self->nes_->get_controller_1(); + + switch ([event keyCode]) + { + case 0x7e: // up arrow + controller_1.get_pressed().add(nes::buttons::up); + break; + case 0x7d: // down arrow + controller_1.get_pressed().add(nes::buttons::down); + break; + case 0x7b: // left arrow + controller_1.get_pressed().add(nes::buttons::left); + break; + case 0x7c: // right arrow + controller_1.get_pressed().add(nes::buttons::right); + break; + case 0x6: // "Z" + controller_1.get_pressed().add(nes::buttons::a); + break; + case 0x7: // "X" + controller_1.get_pressed().add(nes::buttons::b); + break; + case 0x24: // return + controller_1.get_pressed().add(nes::buttons::start); + break; + case 0x31: // space + controller_1.get_pressed().add(nes::buttons::select); + break; + default: + [super keyDown:event]; + break; + } +} + +- (void)keyUp:(NSEvent*)event +{ + auto& controller_1 = self->nes_->get_controller_1(); + + switch ([event keyCode]) + { + case 0x7e: // up arrow + controller_1.get_pressed().remove(nes::buttons::up); + break; + case 0x7d: // down arrow + controller_1.get_pressed().remove(nes::buttons::down); + break; + case 0x7b: // left arrow + controller_1.get_pressed().remove(nes::buttons::left); + break; + case 0x7c: // right arrow + controller_1.get_pressed().remove(nes::buttons::right); + break; + case 0x6: // "Z" + controller_1.get_pressed().remove(nes::buttons::a); + break; + case 0x7: // "X" + controller_1.get_pressed().remove(nes::buttons::b); + break; + case 0x24: // return + controller_1.get_pressed().remove(nes::buttons::start); + break; + case 0x31: // space + controller_1.get_pressed().remove(nes::buttons::select); + break; + default: + [super keyUp:event]; + break; + } +} + +- (void)update:(NSTimeInterval)currentTime +{ + auto const last_time = self->last_time_.value_or(currentTime); + last_time_ = currentTime; + auto const delta_us = static_cast((currentTime - last_time) * 1000. * 1000.); + self->nes_->step(std::chrono::microseconds{ delta_us }); + self->display_->actually_switch_buffers(); +} + +@end \ No newline at end of file -- cgit v1.3.1