OSDN Git Service

add source files.
authorMandhelingFreak <mandheling30-freak@yahoo.co.jp>
Fri, 31 Jan 2014 08:16:28 +0000 (17:16 +0900)
committerMandhelingFreak <mandheling30-freak@yahoo.co.jp>
Fri, 31 Jan 2014 10:05:26 +0000 (19:05 +0900)
25 files changed:
GVONavish/GVONavish.sln [new file with mode: 0644]
GVONavish/GVONavish/GVOConfig.h [new file with mode: 0644]
GVONavish/GVONavish/GVOGameProcess.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVOGameProcess.h [new file with mode: 0644]
GVONavish/GVONavish/GVOImage.h [new file with mode: 0644]
GVONavish/GVONavish/GVONavish.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVONavish.h [new file with mode: 0644]
GVONavish/GVONavish/GVONavish.ico [new file with mode: 0644]
GVONavish/GVONavish/GVONavish.rc [new file with mode: 0644]
GVONavish/GVONavish/GVONavish.vcxproj [new file with mode: 0644]
GVONavish/GVONavish/GVONavish.vcxproj.filters [new file with mode: 0644]
GVONavish/GVONavish/GVOShip.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVOShip.h [new file with mode: 0644]
GVONavish/GVONavish/GVOSurveyCoordExtractor.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVOSurveyCoordExtractor.h [new file with mode: 0644]
GVONavish/GVONavish/GVOWorldMap.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVOWorldMap.h [new file with mode: 0644]
GVONavish/GVONavish/ReadMe.txt [new file with mode: 0644]
GVONavish/GVONavish/Resource.h [new file with mode: 0644]
GVONavish/GVONavish/small.ico [new file with mode: 0644]
GVONavish/GVONavish/stdafx.cpp [new file with mode: 0644]
GVONavish/GVONavish/stdafx.h [new file with mode: 0644]
GVONavish/GVONavish/targetver.h [new file with mode: 0644]
GVONavish/assets/map.png [new file with mode: 0644]
readme.md [new file with mode: 0644]

diff --git a/GVONavish/GVONavish.sln b/GVONavish/GVONavish.sln
new file mode 100644 (file)
index 0000000..160009f
--- /dev/null
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2013 for Windows Desktop
+VisualStudioVersion = 12.0.30110.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVONavish", "GVONavish\GVONavish.vcxproj", "{B5F38077-918C-4B4F-948E-7F756E2ED765}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Release|Win32 = Release|Win32
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {B5F38077-918C-4B4F-948E-7F756E2ED765}.Debug|Win32.ActiveCfg = Debug|Win32
+               {B5F38077-918C-4B4F-948E-7F756E2ED765}.Debug|Win32.Build.0 = Debug|Win32
+               {B5F38077-918C-4B4F-948E-7F756E2ED765}.Release|Win32.ActiveCfg = Release|Win32
+               {B5F38077-918C-4B4F-948E-7F756E2ED765}.Release|Win32.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/GVONavish/GVONavish/GVOConfig.h b/GVONavish/GVONavish/GVOConfig.h
new file mode 100644 (file)
index 0000000..a061b1d
--- /dev/null
@@ -0,0 +1,104 @@
+#pragma once
+#include <Windows.h>
+#include <Shlwapi.h>
+#include <string>
+#include <vector>
+
+
+//!@brief \83\8d\81[\83J\83\8b\90Ý\92è\83t\83@\83C\83\8b\82É\95Û\91\82³\82ê\82é\8fó\91Ô
+class GVOConfig {
+private:
+       const std::wstring m_fileName;
+       const LPCWSTR m_coreSectionName = L"core";
+       const LPCWSTR m_windowSectionName = L"window";
+       const LPCWSTR m_surveyCoordSectionName = L"survey";
+
+public:
+       const LPCWSTR m_defaultMapFileName = L"map.png";
+
+public:
+       std::wstring m_mapFileName;
+       UINT m_pollingInterval;
+       POINT m_windowPos;
+       SIZE m_windowSize;
+       bool m_traceShipPositionEnabled;
+       POINT m_initialSurveyCoord;
+
+       GVOConfig(LPCWSTR fileName)
+               : m_fileName(g_makeFullPath(fileName))
+               , m_pollingInterval(1000)
+               , m_windowPos(defaultPosition())
+               , m_windowSize(defaultSize())
+               , m_traceShipPositionEnabled( true )
+               , m_initialSurveyCoord(defaultSurveyCoord())
+       {
+       }
+       ~GVOConfig()
+       {
+
+       }
+       void save()
+       {
+               LPCWSTR fn = m_fileName.c_str();
+               LPCWSTR section;
+
+               section = m_coreSectionName;
+               ::WritePrivateProfileString( section, L"map", m_mapFileName.c_str(), fn );
+               ::WritePrivateProfileString( section, L"pollingInterval", std::to_wstring( m_pollingInterval ).c_str(), fn );
+               ::WritePrivateProfileString( section, L"traceEnabled", std::to_wstring( m_traceShipPositionEnabled ).c_str(), fn );
+
+               section = m_windowSectionName;
+               ::WritePrivateProfileString(section, L"x", std::to_wstring(m_windowPos.x).c_str(), fn);
+               ::WritePrivateProfileString(section, L"y", std::to_wstring(m_windowPos.y).c_str(), fn);
+               ::WritePrivateProfileString(section, L"cx", std::to_wstring(m_windowSize.cx).c_str(), fn);
+               ::WritePrivateProfileString(section, L"cy", std::to_wstring(m_windowSize.cy).c_str(), fn);
+
+               section = m_surveyCoordSectionName;
+               ::WritePrivateProfileString( section, L"x", std::to_wstring( m_initialSurveyCoord.x ).c_str(), fn );
+               ::WritePrivateProfileString( section, L"y", std::to_wstring( m_initialSurveyCoord.y ).c_str(), fn );
+
+               // \83t\83@\83C\83\8b\8f\91\82«\8fo\82µ
+               ::WritePrivateProfileString(NULL, NULL, NULL, fn);
+       }
+       void load()
+       {
+               LPCWSTR fn = m_fileName.c_str();
+               LPCWSTR section;
+               std::vector<wchar_t> buf(4096);
+
+               section = m_coreSectionName;
+               ::GetPrivateProfileStringW(section, L"map", m_defaultMapFileName, &buf[0], buf.size(), fn);
+               m_mapFileName = &buf[0];
+               m_pollingInterval = ::GetPrivateProfileInt( section, L"polingInterval", m_pollingInterval, fn );
+               m_traceShipPositionEnabled = ::GetPrivateProfileInt( section, L"traceEnabled", m_traceShipPositionEnabled, fn ) != 0;
+
+               section = m_windowSectionName;
+               m_windowPos.x = ::GetPrivateProfileInt(section, L"x", m_windowPos.x, fn);
+               m_windowPos.y = ::GetPrivateProfileInt(section, L"y", m_windowPos.y, fn);
+               m_windowSize.cx = ::GetPrivateProfileInt(section, L"cx", m_windowSize.cx, fn);
+               m_windowSize.cy = ::GetPrivateProfileInt(section, L"cy", m_windowSize.cy, fn);
+
+               section = m_surveyCoordSectionName;
+               m_initialSurveyCoord.x = ::GetPrivateProfileInt( section, L"x", m_initialSurveyCoord.x, fn );
+               m_initialSurveyCoord.y = ::GetPrivateProfileInt( section, L"y", m_initialSurveyCoord.y, fn );
+       }
+
+private:
+       static POINT defaultPosition()
+       {
+               POINT pt = { CW_USEDEFAULT, 0 };
+               return pt;
+       }
+       static SIZE defaultSize()
+       {
+               SIZE size = { CW_USEDEFAULT, 0 };
+               return size;
+       }
+       static POINT defaultSurveyCoord()
+       {
+               // \83\8a\83X\83{\83\93\82Ì\8dÀ\95W
+               POINT p = {15785, 3204};
+               return p;
+       }
+};
+
diff --git a/GVONavish/GVONavish/GVOGameProcess.cpp b/GVONavish/GVONavish/GVOGameProcess.cpp
new file mode 100644 (file)
index 0000000..d5844b7
--- /dev/null
@@ -0,0 +1,142 @@
+#include "stdafx.h"
+#include "GVONavish.h"
+#include "GVOGameProcess.h"
+#include "GVOWorldMap.h"
+#include "GVOSurveyCoordExtractor.h"
+
+
+// \89æ\91\9c\89ð\90Í\83f\83o\83b\83O\97p\81BGVOGameProcess
+//#define GVO_ANALYZE_DEBUG
+
+namespace {
+#ifdef GVO_ANALYZE_DEBUG
+       LPCWSTR const k_debugImageFileName = L"debug5.bmp";
+#endif
+
+       LPWSTR const k_gvoWindowClassName = L"Greate Voyages Online Game MainFrame";
+       LPWSTR const k_gvoWindowCaption = L"\91å\8dq\8aC\8e\9e\91ã Online";
+
+       const POINT k_surveyCoordOffsetFromRightBottom = { 72, 273 };
+       const SIZE k_surveyCoordSize = { 60, 11 };
+};
+
+
+
+void GVOGameProcess::clear()
+{
+       if ( m_process ) {
+               ::CloseHandle( m_process );
+               m_process = NULL;
+       }
+       m_window = NULL;
+}
+
+
+void GVOGameProcess::setConfig( const GVOConfig& config )
+{
+       m_surveyCoord = config.m_initialSurveyCoord;
+}
+
+bool GVOGameProcess::updateState()
+{
+#ifdef GVO_ANALYZE_DEBUG
+       {
+               static bool done = false;
+               if ( !done ) {
+                       done = true;
+               }
+               else {
+                       return false;
+               }
+
+               static GVOImage debugImage;
+               if ( !debugImage.bitmapHandle() ) {
+                       if ( !debugImage.loadFromFile( ::g_makeFullPath( k_debugImageFileName ) ) ) {
+                               ::MessageBox( NULL, L"\83f\83o\83b\83O\83C\83\81\81[\83W\82ª\82È\82¢\82æ", L"\82¦\82ç\81[", MB_ICONERROR );
+                               exit( 0 );
+                       }
+               }
+               HDC hdc = ::GetWindowDC( ::GetDesktopWindow() );
+               HDC hdcMem = ::CreateCompatibleDC( hdc );
+               ::SaveDC( hdcMem );
+               ::SelectObject( hdcMem, debugImage.bitmapHandle() );
+               grabImage( hdcMem, POINT(), debugImage.size() );
+
+               ::RestoreDC( hdcMem, -1 );
+               ::DeleteDC( hdcMem );
+               ::ReleaseDC( NULL, hdc );
+
+               updateSurveyCoord();
+               return true;
+       }
+#endif
+       if ( !m_window ) {
+               m_window = ::FindWindow( k_gvoWindowClassName, k_gvoWindowCaption );
+       }
+       if ( m_window ) {
+               if ( !m_process ) {
+                       DWORD pid = 0;
+                       ::GetWindowThreadProcessId( m_window, &pid );
+                       m_process = ::OpenProcess( SYNCHRONIZE, FALSE, pid );
+               }
+
+               RECT rc;
+               POINT clientOrg = { 0 };
+               ::ClientToScreen( m_window, &clientOrg );
+               ::GetClientRect( m_window, &rc );
+               SIZE size;
+               size.cx = rc.right;
+               size.cy = rc.bottom;
+
+               HDC hdc = ::GetDC( ::GetDesktopWindow() );
+               if ( !hdc ) {
+                       return false;
+               }
+               grabImage( hdc, clientOrg, size );
+               m_timeStamp = ::timeGetTime();
+               ::ReleaseDC( ::GetDesktopWindow(), hdc );
+
+               if ( !updateSurveyCoord() ) {
+                       return false;
+               }
+               return true;
+       }
+       return false;
+}
+
+void GVOGameProcess::grabImage( HDC hdc, const POINT& offset, const SIZE& size )
+{
+       if ( !m_surveyCoordImage.bitmapHandle() ) {
+               m_surveyCoordImage.createDIBImage( k_surveyCoordSize );
+       }
+
+       HDC hdcMem = ::CreateCompatibleDC( hdc );
+       ::SaveDC( hdcMem );
+
+       const int leftEdge = offset.x;
+       const int rightEdge = leftEdge + size.cx;
+       const int topEdge = offset.y;
+       const int bottomEdge = offset.y + size.cy;
+
+       // \91ª\97Ê\8dÀ\95W\83L\83\83\83v\83`\83\83
+       const int xSurvey = rightEdge - k_surveyCoordOffsetFromRightBottom.x;
+       const int ySurvey = bottomEdge - k_surveyCoordOffsetFromRightBottom.y;
+       ::SelectObject( hdcMem, m_surveyCoordImage.bitmapHandle() );
+       ::BitBlt( hdcMem, 0, 0, k_surveyCoordSize.cx, k_surveyCoordSize.cy, hdc, xSurvey, ySurvey, SRCCOPY );
+
+       ::RestoreDC( hdcMem, -1 );
+       ::DeleteDC( hdcMem );
+}
+
+bool GVOGameProcess::updateSurveyCoord()
+{
+       GVOSurveyCoordExtractor extractor( m_surveyCoordImage );
+
+       const std::vector<int>& values = extractor.extractNumbers();
+       if ( values.size() == 2 ) {
+               m_surveyCoord.x = values[0];
+               m_surveyCoord.y = values[1];
+               return true;
+       }
+       return false;
+}
diff --git a/GVONavish/GVONavish/GVOGameProcess.h b/GVONavish/GVONavish/GVOGameProcess.h
new file mode 100644 (file)
index 0000000..b26a537
--- /dev/null
@@ -0,0 +1,78 @@
+#pragma once
+#include "GVOImage.h"
+#include "GVOConfig.h"
+
+
+
+//!@brief \91å\8dq\8aC\8e\9e\91ãOnline\83v\83\8d\83Z\83X
+class GVOGameProcess {
+private:
+       GVOGameProcess( const GVOGameProcess& );
+       GVOGameProcess& operator=(const GVOGameProcess&);
+private:
+       HANDLE m_process;
+       HWND m_window;
+
+       GVOImage m_surveyCoordImage;
+       POINT m_surveyCoord;
+       DWORD m_timeStamp;
+
+public:
+       GVOGameProcess() :
+               m_process( NULL ),
+               m_window( NULL ),
+               m_surveyCoord(),
+               m_timeStamp()
+       {
+       }
+       virtual ~GVOGameProcess()
+       {
+               clear();
+       }
+
+       HANDLE processHandle() const
+       {
+               return m_process;
+       }
+
+       //!@brief \83Q\81[\83\80\83v\83\8d\83Z\83X\82É\8aÖ\82·\82é\83\8a\83\\81[\83X\82ð\89ð\95ú\82·\82é\81B
+       void clear();
+
+       //!@brief \90Ý\92è\8fî\95ñ\82Å\8f\89\8aú\89»\82·\82é
+       void setConfig( const GVOConfig& config );
+
+       //!@brief \83Q\81[\83\80\89æ\96Ê\82ð\93Ç\82Ý\8eæ\82é
+       //!@retval true \93Ç\82Ý\8eæ\82è\90¬\8c÷\81B
+       //!@retval false \93Ç\82Ý\8eæ\82è\8e¸\94s\81B\82¨\82»\82ç\82­\83Q\81[\83\80\82ª\8bN\93®\82µ\82Ä\82¢\82È\82¢\81B
+       bool updateState();
+
+       //!@brief \91ª\97Ê\8dÀ\95W
+       POINT surveyCoord() const
+       {
+               return m_surveyCoord;
+       }
+
+       //!@brief \83Q\81[\83\80\89æ\96Ê\93Ç\82Ý\8eæ\82è\90¬\8c÷\8e\9e\82Ì\8e\9e\8aÔ
+       DWORD timeStamp() const
+       {
+               return m_timeStamp;
+       }
+
+#ifndef NDEBUG
+       //!@brief \83f\83o\83b\83O\97p
+       void setSurveyCoord( const POINT& worldCoord )
+       {
+               m_surveyCoord = worldCoord;
+       }
+       //!@brief \83f\83o\83b\83O\97p\91ª\97Ê\8dÀ\95W\89æ\91\9c
+       const GVOImage& surveyCoordImage() const
+       {
+               return m_surveyCoordImage;
+       }
+#endif
+
+private:
+       void grabImage( HDC hdc, const POINT& offset, const SIZE& size );
+       bool updateSurveyCoord();
+};
+
diff --git a/GVONavish/GVONavish/GVOImage.h b/GVONavish/GVONavish/GVOImage.h
new file mode 100644 (file)
index 0000000..0f81836
--- /dev/null
@@ -0,0 +1,171 @@
+#pragma once
+#include <cstdint>
+#include <memory>
+#include <Windows.h>
+
+// PNG\82Æ\82©\82Ì\93Ç\82Ý\8d\9e\82Ý\97p
+#include <objbase.h>
+#include <gdiplus.h>
+#include <gdiplusbitmap.h>
+
+
+class GVOImage {
+private:
+       GVOImage( const GVOImage& );
+       GVOImage& operator=(const GVOImage&);
+
+private:
+       std::wstring m_fileName;
+       HBITMAP m_hbmp;
+       SIZE m_size;
+       uint8_t * m_bits;
+
+public:
+       GVOImage() : m_hbmp(), m_size(), m_bits()
+       {
+       }
+       ~GVOImage()
+       {
+               reset();
+       }
+       void reset()
+       {
+               if ( m_hbmp ) {
+                       ::DeleteObject( m_hbmp );
+                       m_hbmp = NULL;
+               }
+               m_fileName = L"";
+       }
+       bool stretchCopy( const GVOImage& src, const SIZE& size )
+       {
+               return stretchCopy( src, size.cx, size.cy );
+       }
+       bool stretchCopy( const GVOImage& src, uint32_t width, uint32_t height )
+       {
+               const SIZE size = { width, height };
+               if ( !createDIBImage( size ) ) {
+                       return false;
+               }
+
+               HDC hdc = ::GetDC( NULL );
+               HDC hdcSrc = ::CreateCompatibleDC( hdc );
+               HDC hdcDst = ::CreateCompatibleDC( hdc );
+
+               ::SaveDC( hdcSrc );
+               ::SaveDC( hdcDst );
+               ::SelectObject( hdcDst, m_hbmp );
+               ::SelectObject( hdcSrc, src.m_hbmp );
+
+               if ( m_size.cx != src.m_size.cx || m_size.cy != src.m_size.cy ) {
+                       POINT org;
+                       ::GetBrushOrgEx( hdcDst, &org );
+                       ::SetStretchBltMode( hdcDst, HALFTONE );
+                       ::SetBrushOrgEx( hdcDst, org.x, org.y, NULL );
+
+                       ::StretchBlt( hdcDst, 0, 0, m_size.cx, m_size.cy,
+                               hdcSrc, 0, 0, src.m_size.cx, src.m_size.cy,
+                               SRCCOPY );
+               }
+               else {
+                       ::BitBlt( hdcDst, 0, 0, m_size.cx, m_size.cy,
+                               hdcSrc, 0, 0, SRCCOPY );
+               }
+
+               ::RestoreDC( hdcSrc, -1 );
+               ::DeleteDC( hdcSrc );
+               ::RestoreDC( hdcDst, -1 );
+               ::DeleteDC( hdcDst );
+               ::ReleaseDC( NULL, hdc );
+               return true;
+       }
+       bool isCompatible( const SIZE& size ) const
+       {
+               if ( !m_hbmp ) {
+                       return false;
+               }
+               if ( m_size.cx != size.cx || m_size.cy != size.cy ) {
+                       return false;
+               }
+               return true;
+       }
+       HBITMAP bitmapHandle() const
+       {
+               return m_hbmp;
+       }
+       const SIZE& size() const
+       {
+               return m_size;
+       }
+       LONG width() const
+       {
+               return m_size.cx;
+       }
+       LONG height() const
+       {
+               return m_size.cy;
+       }
+       const uint8_t * imageBits() const
+       {
+               return m_bits;
+       }
+       uint8_t * mutableImageBits()
+       {
+               return m_bits;
+       }
+       const std::wstring& fileName() const
+       {
+               return m_fileName;
+       }
+
+       bool createDIBImage( int width, int height )
+       {
+               reset();
+
+               BITMAPINFOHEADER bmih = { sizeof(bmih) };
+               bmih.biWidth = width;
+               bmih.biHeight = -height;
+               bmih.biPlanes = 1;
+               bmih.biBitCount = 24;
+               bmih.biSizeImage = width * height * 3;
+               m_hbmp = ::CreateDIBSection( NULL, (LPBITMAPINFO)&bmih, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0 );
+               if ( !m_hbmp ) {
+                       return false;
+               }
+               m_size.cx = width;
+               m_size.cy = height;
+               return true;
+       }
+       bool createDIBImage( const SIZE& size )
+       {
+               reset();
+
+               BITMAPINFOHEADER bmih = { sizeof(bmih) };
+               bmih.biWidth = size.cx;
+               bmih.biHeight = -size.cy;
+               bmih.biPlanes = 1;
+               bmih.biBitCount = 24;
+               bmih.biSizeImage = bmih.biWidth * bmih.biHeight * 3;
+               m_hbmp = ::CreateDIBSection( NULL, (LPBITMAPINFO)&bmih, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0 );
+               if ( !m_hbmp ) {
+                       return false;
+               }
+               m_size = size;
+               return true;
+       }
+       bool loadFromFile( const std::wstring& fileName )
+       {
+               std::auto_ptr<Gdiplus::Bitmap> image;
+               image.reset( Gdiplus::Bitmap::FromFile( fileName.c_str() ) );
+               image->GetHBITMAP( Gdiplus::Color( 0, 0, 0 ), &m_hbmp );
+               image.reset();
+               if ( !m_hbmp ) {
+                       return false;
+               }
+               BITMAP bmp = { 0 };
+               ::GetObject( m_hbmp, sizeof(bmp), &bmp );
+               m_size.cx = bmp.bmWidth;
+               m_size.cy = ::abs( bmp.bmHeight );
+               m_fileName = fileName;
+               return true;
+       }
+};
diff --git a/GVONavish/GVONavish/GVONavish.cpp b/GVONavish/GVONavish/GVONavish.cpp
new file mode 100644 (file)
index 0000000..2cf1f9a
--- /dev/null
@@ -0,0 +1,675 @@
+#include "stdafx.h"
+#include <vector>
+#include <list>
+
+// PNG\82Æ\82©\82Ì\93Ç\82Ý\8d\9e\82Ý\97p
+#include <gdiplus.h>
+#pragma comment(lib, "gdiplus.lib")
+
+// PATH API\97p
+#include <Shlwapi.h>
+#pragma comment(lib, "shlwapi.lib")
+
+#include "GVONavish.h"
+#include "GVOConfig.h"
+#include "GVOGameProcess.h"
+#include "GVOWorldMap.h"
+#include "GVOShip.h"
+
+
+
+// \93K\93\96\82É\93®\82«\89ñ\82é\83f\83o\83b\83O\83R\81[\83h
+//#define DEBUG_AUTO_CRUISE
+
+
+
+// \83^\83C\83}\81[ID
+enum : UINT_PTR {
+       GVO_TIMER_ID_INVALID_VALUE = 0,
+       GVO_TIMER_ID_POOLING,
+};
+
+
+// \83O\83\8d\81[\83o\83\8b\95Ï\90\94:
+HINSTANCE g_hinst;                                                                             // \8c»\8dÝ\82Ì\83C\83\93\83^\81[\83t\83F\83C\83X
+
+
+// \8aÖ\90\94\83v\83\8d\83g\83^\83C\83v\90é\8c¾
+static ATOM MyRegisterClass( HINSTANCE hInstance );
+static BOOL InitInstance( HINSTANCE, int );
+LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
+static LRESULT s_mainLoop();
+
+
+// \83\81\83b\83Z\81[\83W\83n\83\93\83h\83\89
+static bool s_onCreate( HWND, LPCREATESTRUCT );
+static bool s_onTimer( HWND, UINT_PTR, TIMERPROC );
+static void s_onMove( HWND, WORD, WORD );
+static void s_onSize( HWND, UINT, WORD, WORD );
+static void s_onMouseWheel( HWND, int16_t, UINT, int16_t, int16_t );
+static void s_onMouseMove( HWND, UINT, int16_t, int16_t );
+static void s_onMouseLeftButtonDown( HWND, UINT, int16_t, int16_t );
+static void s_onMouseLeftButtonUp( HWND, UINT, int16_t, int16_t );
+static void s_onMouseLeftButtonDoubleClick( HWND, UINT, int16_t, int16_t );
+static void s_onMouseRightButtonUp( HWND, UINT, int16_t, int16_t );
+static void s_onPaint( HWND );
+
+
+// \83A\83v\83\8a\8f\88\97\9d
+static std::wstring s_makeVersionString();
+static void s_updateWindowTitle( HWND );
+static void s_popupMenu( HWND, int16_t, int16_t );
+static void s_popupCoord( HWND, int16_t, int16_t );
+
+
+// \83\8d\81[\83J\83\8b\95Ï\90\94
+static LPCWSTR const k_appName = L"GVANavi\81i\82Á\82Û\82¢\89½\82©\81j";     // \83A\83v\83\8a\83P\81[\83V\83\87\83\93\96¼
+static LPCWSTR const k_version = L"ver 1.0\83¿1";        // \83o\81[\83W\83\87\83\93\94Ô\8d\86
+static LPCWSTR const k_copyright = L"copyright(c) mandheling"; // \92\98\8dì\8c \95\\8e¦\81i\82¢\82¿\82¨\81[\81j
+
+static LPCWSTR const k_windowClassName = L"GVANavish";         // \83\81\83C\83\93 \83E\83B\83\93\83h\83\83N\83\89\83X\96¼
+static const LPCWSTR k_configFileName = L"GVONavish.ini";      // \90Ý\92è\83t\83@\83C\83\8b\96¼
+
+static const std::wstring k_aboutText = s_makeVersionString(); // \83o\81[\83W\83\87\83\93\8fî\95ñ\83e\83L\83X\83g
+
+static GVOImage s_backbuffer;  //!<@brief \95`\89æ\97p24bit\83C\83\81\81[\83W\83o\83b\83t\83@
+
+static Gdiplus::GdiplusStartupInput s_gdisi;
+static ULONG_PTR s_gdiToken;
+
+static GVOConfig s_config( k_configFileName );
+static GVOGameProcess s_gvoGameProcess;
+static GVOWorldMap s_worldMap;
+static GVOShip s_shipVector;
+
+static UINT s_pollingInterval = 1000;  // \8fó\91Ô\8aÄ\8e\8b\8aÔ\8au\81i1\95b\81j
+static bool s_isUpdated = false;               // \8fó\91Ô\8dX\90V\83t\83\89\83O
+static bool s_isDragging = false;              // \83h\83\89\83b\83O\8fó\91Ô\83t\83\89\83O
+static SIZE s_clientSize;                              // \83N\83\89\83C\83A\83\93\83g\97Ì\88æ\82Ì\91å\82«\82³
+static POINT s_dragOrg;                                        // \83h\83\89\83b\83O\8c´\93_\81i\88Ú\93®\97Ê\8eZ\8fo\97p\81j
+
+
+// \83f\83o\83b\83O\97p\8e©\93®\8dq\8ds\95Ï\90\94
+#if defined( DEBUG_AUTO_CRUISE ) && !defined(NDEBUG)
+static double xAutoCruise = s_config.m_initialSurveyCoord.x;
+static double yAutoCruise = s_config.m_initialSurveyCoord.y;
+static const double k_AutoCruiseLength = s_knotFromWorldCoordPoint( 10 );
+static const int k_autoCruiseAngle = 32;
+static const DWORD k_autoCruiseTurnInterval = 5000*3;
+
+static double s_autoCruiseAngle = 0;
+#endif // #ifdef DEBUG_AUTO_CRUISE
+
+
+
+
+
+
+
+
+int APIENTRY _tWinMain( _In_ HINSTANCE hInstance,
+       _In_opt_ HINSTANCE hPrevInstance,
+       _In_ LPTSTR    lpCmdLine,
+       _In_ int       nCmdShow )
+{
+       UNREFERENCED_PARAMETER( hPrevInstance );
+       UNREFERENCED_PARAMETER( lpCmdLine );
+
+       ::timeBeginPeriod( 1 );
+       GdiplusStartup( &s_gdiToken, &s_gdisi, NULL );
+       s_config.load();
+
+       MyRegisterClass( hInstance );
+
+       // \83A\83v\83\8a\83P\81[\83V\83\87\83\93\82Ì\8f\89\8aú\89»\82ð\8eÀ\8ds\82µ\82Ü\82·:
+       if ( !InitInstance( hInstance, nCmdShow ) ) {
+               return 0;
+       }
+
+       // \83\81\83C\83\93 \83\81\83b\83Z\81[\83\83\8b\81[\83v:
+       const LRESULT retVal = s_mainLoop();
+
+       s_config.save();
+       Gdiplus::GdiplusShutdown( s_gdiToken );
+       ::timeEndPeriod( 1 );
+       return retVal;
+}
+
+static ATOM MyRegisterClass( HINSTANCE hInstance )
+{
+       WNDCLASSEX wcex = { sizeof(wcex) };
+
+       wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
+       wcex.lpfnWndProc = WndProc;
+       wcex.hInstance = hInstance;
+       wcex.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE( IDR_MAINFRAME ) );
+       wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
+       wcex.hbrBackground = (HBRUSH)::GetStockObject( BLACK_BRUSH );
+       //wcex.lpszMenuName     = MAKEINTRESOURCE(IDC_GVONAVISH);
+       wcex.lpszClassName = k_windowClassName;
+       wcex.hIconSm = LoadIcon( wcex.hInstance, MAKEINTRESOURCE( IDI_SMALL ) );
+
+       return RegisterClassEx( &wcex );
+}
+
+static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
+{
+       if ( !s_worldMap.loadFromFile( s_config ) ) {
+               std::vector<wchar_t> desc( 4096 );
+               ::swprintf( &desc[0], desc.size(), L"\83}\83b\83v\89æ\91\9c\83t\83@\83C\83\8b\82ª\82 \82è\82Ü\82¹\82ñ\81B\n"
+                       L"\83A\83v\83\8a\82Æ\93¯\82\83t\83H\83\8b\83_\82É%s\82ð\97p\88Ó\82µ\82Ä\82­\82¾\82³\82¢\81B",
+                       s_config.m_defaultMapFileName );
+               ::MessageBox( NULL,
+                       &desc[0],
+                       k_appName,
+                       MB_ICONERROR | MB_SETFOREGROUND | MB_OK );
+               return FALSE;
+       }
+
+       HWND hwnd;
+
+       g_hinst = hInstance; // \83O\83\8d\81[\83o\83\8b\95Ï\90\94\82É\83C\83\93\83X\83^\83\93\83X\8f\88\97\9d\82ð\8ai\94[\82µ\82Ü\82·\81B
+
+       hwnd = CreateWindow( k_windowClassName, k_appName, WS_OVERLAPPEDWINDOW,
+               s_config.m_windowPos.x, s_config.m_windowPos.y,
+               s_config.m_windowSize.cx, s_config.m_windowSize.cy,
+               NULL, NULL, hInstance, NULL );
+
+       if ( !hwnd ) {
+               return FALSE;
+       }
+
+       s_pollingInterval = s_config.m_pollingInterval;
+       s_gvoGameProcess.setConfig( s_config );
+       s_worldMap.setConfig( s_config );
+
+       if ( s_gvoGameProcess.updateState() ) {
+               s_shipVector.updateWithSurveyCoord( s_gvoGameProcess.surveyCoord() );
+               s_worldMap.setShipPosition( s_gvoGameProcess.surveyCoord(), s_config.m_traceShipPositionEnabled );
+       }
+       s_updateWindowTitle( hwnd );
+
+       ShowWindow( hwnd, nCmdShow );
+       UpdateWindow( hwnd );
+
+       return TRUE;
+}
+
+static LRESULT s_mainLoop()
+{
+       MSG msg;
+       HACCEL hAccelTable;
+
+       hAccelTable = LoadAccelerators( g_hinst, MAKEINTRESOURCE( IDC_GVONAVISH ) );
+
+       for ( ;; ) {
+               if ( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
+                       if ( msg.message == WM_QUIT ) {
+                               break;
+                       }
+                       if ( !TranslateAccelerator( msg.hwnd, hAccelTable, &msg ) ) {
+                               TranslateMessage( &msg );
+                               DispatchMessage( &msg );
+                       }
+                       continue;
+               }
+               std::vector<HANDLE> handles;
+
+               // \8aÄ\8e\8b\82·\82é\83n\83\93\83h\83\8b\82ð\92Ç\89Á\82·\82é\81B
+               if ( s_gvoGameProcess.processHandle() ) {
+                       handles.push_back( s_gvoGameProcess.processHandle() );
+               }
+
+               if ( handles.empty() ) {
+                       ::WaitMessage();
+                       continue;
+               }
+
+               DWORD const waitResult = ::MsgWaitForMultipleObjects( handles.size(), &handles[0], FALSE, INFINITE, QS_ALLINPUT );
+               if ( handles.size() <= waitResult ) {
+                       continue;
+               }
+
+               // \8aÄ\8e\8b\82·\82é\83n\83\93\83h\83\8b\82É\91Î\89\9e\82·\82é\81B
+               HANDLE const activeHandle = handles[waitResult];
+
+               if ( activeHandle == s_gvoGameProcess.processHandle() ) {
+                       // \83Q\81[\83\80\83v\83\8d\83Z\83X\82ª\8fI\97¹\82µ\82½\81B
+                       s_gvoGameProcess.clear();
+                       continue;
+               }
+       }
+       return (int)msg.wParam;
+}
+
+
+LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
+{
+       int wmId, wmEvent;
+
+       switch ( message ) {
+       case WM_TIMER:
+               return !s_onTimer( hwnd, wp, reinterpret_cast<TIMERPROC>(lp) );
+
+       case WM_ERASEBKGND:
+               return TRUE;
+       case WM_PAINT:
+               s_onPaint( hwnd );
+               break;
+
+       case WM_MOVE:
+               s_onMove( hwnd, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+       case WM_SIZE:
+               s_onSize( hwnd, wp, LOWORD( lp ), HIWORD( lp ) );
+               break;
+
+       case WM_COMMAND:
+               wmId = LOWORD( wp );
+               wmEvent = HIWORD( wp );
+               // \91I\91ð\82³\82ê\82½\83\81\83j\83\85\81[\82Ì\89ð\90Í:
+               switch ( wmId ) {
+               case IDM_ABOUT:
+                       ::MessageBox( hwnd,
+                               k_aboutText.c_str(),
+                               k_appName,
+                               MB_OK | MB_ICONINFORMATION );
+                       break;
+               case IDM_EXIT:
+                       DestroyWindow( hwnd );
+                       break;
+               case IDM_TOGGLE_TRACE_SHIP:
+                       s_config.m_traceShipPositionEnabled = !s_config.m_traceShipPositionEnabled;
+                       break;
+               case IDM_ERASE_SHIP_ROUTE:
+                       s_worldMap.clearShipRoute();
+                       break;
+               default:
+                       return DefWindowProc( hwnd, message, wp, lp );
+               }
+               break;
+
+       case WM_MOUSEWHEEL:
+               s_onMouseWheel( hwnd, HIWORD( wp ), LOWORD( wp ), int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+       case WM_MOUSEMOVE:
+               s_onMouseMove( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+       case WM_LBUTTONDOWN:
+               s_onMouseLeftButtonDown( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+       case WM_LBUTTONUP:
+               s_onMouseLeftButtonUp( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+       case WM_RBUTTONUP:
+               s_onMouseRightButtonUp( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+       case WM_LBUTTONDBLCLK:
+               s_onMouseLeftButtonDoubleClick( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
+               break;
+
+       case WM_CREATE:
+               if ( !s_onCreate( hwnd, reinterpret_cast<LPCREATESTRUCT>(lp) ) ) {
+                       return -1;
+               }
+               break;
+       case WM_DESTROY:
+               PostQuitMessage( 0 );
+               break;
+       default:
+               return DefWindowProc( hwnd, message, wp, lp );
+       }
+       return 0;
+}
+
+
+static bool s_onCreate( HWND hwnd, LPCREATESTRUCT /*cs*/ )
+{
+       ::SetTimer( hwnd, GVO_TIMER_ID_POOLING, s_pollingInterval, NULL );
+       return true;
+}
+
+
+static bool s_onTimer( HWND hwnd, UINT_PTR timerID, TIMERPROC /*timerFunction*/ )
+{
+       if ( timerID != GVO_TIMER_ID_POOLING ) {
+               return false;
+       }
+
+#if defined( DEBUG_AUTO_CRUISE ) && !defined(NDEBUG)
+       {
+               static bool isRandInitialized = false;
+               if ( !isRandInitialized ) {
+                       srand( ::timeGetTime() );
+                       isRandInitialized = true;
+
+                       // \8am\94F\91¬\93x\82ð\8fã\82°\82½\82¢\82Æ\82«\97p
+                       //::SetTimer( hwnd, GVO_TIMER_ID_POOLING, 1, NULL );
+                       //::SetTimer( hwnd, GVO_TIMER_ID_POOLING, 100, NULL );
+               }
+
+               const double rad = ((s_autoCruiseAngle) * M_PI) / 180;
+               const double vx = ::cos( rad );
+               const double vy = ::sin( rad );
+
+               xAutoCruise += vx * k_AutoCruiseLength;
+               yAutoCruise += vy * k_AutoCruiseLength;
+
+               static DWORD tick = ::timeGetTime();
+               if ( (tick + k_autoCruiseTurnInterval) < ::timeGetTime() ) {
+                       if ( rand() & 0x100 ) {
+                               s_autoCruiseAngle += 90 + (rand() / double(RAND_MAX));
+                       }
+                       else {
+                               s_autoCruiseAngle += (rand() & 1) ? k_autoCruiseAngle : -k_autoCruiseAngle;
+                       }
+                       tick = ::timeGetTime();
+               }
+               s_autoCruiseAngle = fmod(::fabs( s_autoCruiseAngle ), 360);
+
+               if ( xAutoCruise < 0 ) {
+                       xAutoCruise += GVOWorldMap::k_worldWidth;
+               }
+               if ( yAutoCruise < 0 ) {
+                       yAutoCruise += GVOWorldMap::k_worldHeight;
+               }
+               xAutoCruise = fmod( xAutoCruise, (double)GVOWorldMap::k_worldWidth );
+               yAutoCruise = fmod( yAutoCruise, (double)GVOWorldMap::k_worldHeight );
+
+               //// \92n\90}\82ð\8c×\82®\8f\88\97\9d\82Ì\8am\94F\97p\83f\83o\83b\83O\83R\81[\83h
+               //if ( 100 <= xAutoCruise && xAutoCruise <= (GVOWorldMap::k_worldWidth - 100) ) {
+               //      xAutoCruise = 0;
+               //}
+
+
+               POINT p = {
+                       LONG( xAutoCruise ),
+                       LONG( yAutoCruise )
+               };
+               s_shipVector.updateWithSurveyCoord( p, ::timeGetTime() );
+               s_gvoGameProcess.setSurveyCoord( p );
+               s_worldMap.setShipPosition( p, s_config.m_traceShipPositionEnabled );
+               HDC hdc = ::GetDC( hwnd );
+               s_worldMap.updateShipRouteMap( hdc );
+               ::ReleaseDC( hwnd, hdc );
+               s_updateWindowTitle( hwnd );
+               ::InvalidateRect( hwnd, NULL, FALSE);
+               return true;
+       }
+#endif // #ifdef DEBUG_AUTO_CRUISE
+
+       s_isUpdated = s_gvoGameProcess.updateState();
+
+       if ( s_isUpdated ) {
+               s_config.m_initialSurveyCoord = s_gvoGameProcess.surveyCoord();
+               s_shipVector.updateWithSurveyCoord( s_gvoGameProcess.surveyCoord() );
+               s_worldMap.setShipPosition( s_gvoGameProcess.surveyCoord(), s_config.m_traceShipPositionEnabled );
+               HDC hdc = ::GetDC( hwnd );
+               s_worldMap.updateShipRouteMap( hdc );
+               ::ReleaseDC( hwnd, hdc );
+               s_updateWindowTitle( hwnd );
+               ::InvalidateRect( hwnd, NULL, FALSE );
+       }
+
+       return true;
+}
+
+
+static void s_onMove( HWND hwnd, WORD /*cx*/, WORD /*cy*/ )
+{
+       const DWORD style = ::GetWindowLong( hwnd, GWL_STYLE );
+       if ( style & WS_MAXIMIZE ) {
+               return;
+       }
+       RECT rc = { 0 };
+       ::GetWindowRect( hwnd, &rc );
+       s_config.m_windowPos.x = rc.left;
+       s_config.m_windowPos.y = rc.top;
+}
+
+
+static void s_onSize( HWND hwnd, UINT state, WORD cx, WORD cy )
+{
+       RECT rc = { 0 };
+
+       switch ( state ) {
+       case SIZE_RESTORED:
+               ::GetWindowRect( hwnd, &rc );
+               s_config.m_windowSize.cx = rc.right - rc.left;
+               s_config.m_windowSize.cy = rc.bottom - rc.top;
+               break;
+       case SIZE_MAXIMIZED:
+               break;
+       default:
+               return;
+       }
+
+       if ( s_clientSize.cx != cx || s_clientSize.cy != cy ) {
+               s_clientSize.cx = cx;
+               s_clientSize.cy = cy;
+               if ( !s_backbuffer.isCompatible( s_clientSize ) ) {
+                       s_backbuffer.createDIBImage( s_clientSize );
+               }
+               s_worldMap.setViewSize( s_clientSize );
+       }
+}
+
+
+static void s_onMouseWheel( HWND hwnd, int16_t delta, UINT vkey, int16_t x, int16_t y )
+{
+       bool isChanged = false;
+
+       if ( 0 < delta ) {
+               isChanged = s_worldMap.zoomIn();
+       }
+       else {
+               isChanged = s_worldMap.zoomOut();
+       }
+
+       if ( isChanged ) {
+               s_updateWindowTitle( hwnd );
+               ::InvalidateRect( hwnd, NULL, FALSE );
+       }
+}
+
+
+static void s_onMouseMove( HWND hwnd, UINT vkey, int16_t x, int16_t y )
+{
+       if ( s_isDragging ) {
+               const int dx = x - s_dragOrg.x;
+               const int dy = y - s_dragOrg.y;
+               const int threshold = 1;        // \8a´\93x\82ª\97Ç\82·\82¬\82é\82Æ\92Ç\8f]\82ª\8aÈ\92P\82É\90Ø\82ê\82Ä\82µ\82Ü\82¤\82Ì\82Å\93K\93\96\82É\91Î\8dô
+               if ( s_config.m_traceShipPositionEnabled ) {
+                       if ( ::abs( dx ) <= threshold && ::abs( dy ) < threshold ) {
+                               return;
+                       }
+               }
+               const POINT offset = { -dx, -dy };
+
+               s_worldMap.offsetFocusInViewCoord( offset );
+               ::InvalidateRect( hwnd, NULL, FALSE );
+
+               s_dragOrg.x = x;
+               s_dragOrg.y = y;
+               s_config.m_traceShipPositionEnabled = false;
+       }
+       else {
+
+       }
+}
+
+
+static void s_onMouseLeftButtonDown( HWND hwnd, UINT vkey, int16_t x, int16_t y )
+{
+       if ( s_isDragging ) {
+
+       }
+       else {
+               ::SetCapture( hwnd );
+               s_isDragging = true;
+               s_dragOrg.x = x;
+               s_dragOrg.y = y;
+       }
+}
+
+
+static void s_onMouseLeftButtonUp( HWND hwnd, UINT vkey, int16_t x, int16_t y )
+{
+       if ( s_isDragging ) {
+               ::ReleaseCapture();
+               s_isDragging = false;
+               s_dragOrg.x = 0;
+               s_dragOrg.y = 0;
+       }
+       else {
+
+       }
+}
+
+
+static void s_onMouseLeftButtonDoubleClick( HWND hwnd, UINT vkey, int16_t x, int16_t y )
+{
+       if ( s_isDragging ) {
+
+       }
+       else {
+               s_popupCoord( hwnd, x, y );
+       }
+}
+
+
+static void s_onMouseRightButtonUp( HWND hwnd, UINT vkey, int16_t x, int16_t y )
+{
+       if ( s_isDragging ) {
+
+       }
+       else {
+               s_popupMenu( hwnd, x, y );
+       }
+}
+
+
+// \83_\83u\83\8b\83o\83b\83t\83@\83\8a\83\93\83O\82Å\82¿\82ç\82Â\82«\96h\8e~\81B
+static void s_onPaint( HWND hwnd )
+{
+#ifdef GVO_PERF_CHECK
+       int64_t perfBegin = 0, perfEnd = 0;
+       ::QueryPerformanceCounter((LARGE_INTEGER*)&perfBegin);
+#endif
+       if ( !s_backbuffer.bitmapHandle() ) {
+               s_backbuffer.createDIBImage( s_clientSize );
+       }
+
+       PAINTSTRUCT ps;
+       HDC hdc = BeginPaint( hwnd, &ps );
+       HDC hdcBackbuffer = ::CreateCompatibleDC( hdc );
+       ::SaveDC( hdcBackbuffer );
+       ::SelectObject( hdcBackbuffer, s_backbuffer.bitmapHandle() );
+       RECT rc = { 0, 0, s_clientSize.cx, s_clientSize.cy };
+       ::FillRect( hdcBackbuffer, &rc, (HBRUSH)::GetStockObject( BLACK_BRUSH ) );
+
+       // \95`\89æ\82ðhdcBackbuffer\82É\91Î\82µ\82Ä\8ds\82¤\81B
+       s_worldMap.drawMap( hdcBackbuffer, s_shipVector );
+
+#ifndef NDEBUG
+       // \91ª\97Ê\8dÀ\95W\82ð\95`\89æ
+       const GVOImage& surveyCoordImage = s_gvoGameProcess.surveyCoordImage();
+       HDC hdcSurvey = ::CreateCompatibleDC( hdcBackbuffer );
+       ::SaveDC( hdcSurvey );
+       ::SelectObject( hdcSurvey, surveyCoordImage.bitmapHandle() );
+       ::BitBlt( hdcBackbuffer, 0, 0, surveyCoordImage.size().cx, surveyCoordImage.size().cy,
+               hdcSurvey, 0, 0, SRCCOPY );
+       ::RestoreDC( hdcSurvey, -1 );
+       ::DeleteDC( hdcSurvey );
+#endif
+
+       ::BitBlt( hdc, 0, 0, s_clientSize.cx, s_clientSize.cy,
+               hdcBackbuffer, 0, 0, SRCCOPY );
+
+#if defined( DEBUG_AUTO_CRUISE ) && !defined(NDEBUG)
+       {
+               const double rad = ((s_autoCruiseAngle)* M_PI) / 180;
+               const double vx = ::cos( rad );
+               const double vy = ::sin( rad );
+               const LONG length = max( s_clientSize.cx, s_clientSize.cy );
+
+               LONG x2 = s_clientSize.cx / 2 + LONG( vx * length );
+               LONG y2 = s_clientSize.cy / 2 + LONG( vy * length );
+               HDC hdc = ::GetDC( hwnd );
+               HPEN pen = ::CreatePen( PS_SOLID, 3, RGB( 255, 255, 0 ) );
+               HGDIOBJ old = ::SelectObject( hdc, pen );
+               ::MoveToEx( hdc, s_clientSize.cx / 2, s_clientSize.cy / 2, NULL );
+               ::LineTo( hdc, x2, y2 );
+               ::SelectObject( hdc, old );
+               ::DeleteObject( pen );
+               ::ReleaseDC( hwnd, hdc );
+       }
+#endif // #ifdef DEBUG_AUTO_CRUISE
+
+       ::RestoreDC( hdcBackbuffer, -1 );
+       ::DeleteDC( hdcBackbuffer );
+       EndPaint( hwnd, &ps );
+
+
+#ifdef GVO_PERF_CHECK
+       ::QueryPerformanceCounter((LARGE_INTEGER*)&perfEnd);
+       int64_t freq = 0;
+       ::QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
+       const double deltaPerSec = (double(perfEnd - perfBegin) / double(freq)) * 1000.0;
+       typedef std::list<double> PerfList;
+       static PerfList perf;
+       perf.push_back(deltaPerSec);
+
+       const double ave = std::accumulate( perf.begin(), perf.end(), 0.0 ) / perf.size();
+       if ( 100 < perf.size() ) {
+               perf.pop_front();
+       }
+
+       std::wstring s;
+       s = std::wstring( L"perf:" ) + std::to_wstring( ave ) + L"(ms)\n";
+       ::SetWindowText( hwnd, s.c_str() );
+       ::InvalidateRect( hwnd, NULL, FALSE );
+#endif
+}
+
+
+static std::wstring s_makeVersionString()
+{
+       std::wstring s;
+       s += std::wstring(k_appName) + L" " + k_version + L"\n";
+       s += k_copyright;
+       return s;
+}
+
+
+static void s_updateWindowTitle( HWND hwnd )
+{
+       const POINT& surveyCoord = s_gvoGameProcess.surveyCoord();
+
+       std::vector<wchar_t> buf( 4096 );
+       ::swprintf( &buf[0], buf.size(), L"%d,%d - (%.1f%%) - %s %s",
+               surveyCoord.x, surveyCoord.y,
+               s_worldMap.viewScale() * s_worldMap.viewScaleOrder(),
+               k_appName, k_version
+               );
+       ::SetWindowText( hwnd, &buf[0] );
+}
+
+
+static void s_popupMenu( HWND hwnd, int16_t x, int16_t y )
+{
+       HMENU hmenu = ::LoadMenu( g_hinst, MAKEINTRESOURCE( IDC_POPUPMENU ) );
+       HMENU popupMenu = ::GetSubMenu( hmenu, 0 );
+
+       ::CheckMenuItem( popupMenu, IDM_TOGGLE_TRACE_SHIP, s_config.m_traceShipPositionEnabled ? MF_CHECKED : MF_UNCHECKED );
+
+       POINT p = { x, y };
+       ::ClientToScreen( hwnd, &p );
+       ::TrackPopupMenu( popupMenu, TPM_NONOTIFY | TPM_LEFTALIGN | TPM_TOPALIGN, p.x, p.y, 0, hwnd, NULL );
+}
+
+
+static void s_popupCoord( HWND hwnd, int16_t x, int16_t y )
+{
+
+}
diff --git a/GVONavish/GVONavish/GVONavish.h b/GVONavish/GVONavish/GVONavish.h
new file mode 100644 (file)
index 0000000..3e5676a
--- /dev/null
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "resource.h"
+#include <Shlwapi.h>
+#include <string>
+
+
+// \83f\83o\83b\83O\8e\9e\82Ì\95`\89æ\83p\83t\83H\81[\83}\83\93\83X\91ª\92è\97p\81B
+
+//#define GVO_PERF_CHECK
+
+
+inline std::wstring g_makeFullPath(const std::wstring& fileName)
+{
+       wchar_t filePath[MAX_PATH] = { 0 };
+
+       if (::PathIsRelative(fileName.c_str())) {
+               wchar_t dir[MAX_PATH] = { 0 };
+               ::GetModuleFileName(::GetModuleHandle(NULL), dir, _countof(dir));
+               ::PathRemoveFileSpec(dir);
+               ::PathCombine(filePath, dir, fileName.c_str());
+       }
+       else {
+               ::lstrcpy(filePath, fileName.c_str());
+       }
+       return filePath;
+}
diff --git a/GVONavish/GVONavish/GVONavish.ico b/GVONavish/GVONavish/GVONavish.ico
new file mode 100644 (file)
index 0000000..449296f
Binary files /dev/null and b/GVONavish/GVONavish/GVONavish.ico differ
diff --git a/GVONavish/GVONavish/GVONavish.rc b/GVONavish/GVONavish/GVONavish.rc
new file mode 100644 (file)
index 0000000..50f4592
Binary files /dev/null and b/GVONavish/GVONavish/GVONavish.rc differ
diff --git a/GVONavish/GVONavish/GVONavish.vcxproj b/GVONavish/GVONavish/GVONavish.vcxproj
new file mode 100644 (file)
index 0000000..17a3ac0
--- /dev/null
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B5F38077-918C-4B4F-948E-7F756E2ED765}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>GVONavish</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120_xp</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120_xp</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+    <PostBuildEvent>
+      <Command>if not exist "$(SolutionDir)archive" mkdir "$(SolutionDir)archive"
+if not exist "$(SolutionDir)archive\$(ProjectName)" mkdir "$(SolutionDir)archive\$(ProjectName)"
+if not exist "$(SolutionDir)archive\$(ProjectName)\map.png" copy /Y "$(SolutionDir)assets\map.png" "$(SolutionDir)archive\$(ProjectName)\map.png"
+copy /Y "$(TargetPath)" "$(SolutionDir)archive\$(ProjectName)\$(TargetFileName)"
+</Command>
+      <Message>実行ファイルをフォルダにコピーしています...</Message>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <DebugInformationFormat>None</DebugInformationFormat>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+    <PostBuildEvent>
+      <Command>echo if not exist "$(SolutionDir)archive" mkdir "$(SolutionDir)archive"
+echo if not exist "$(SolutionDir)archive\$(ProjectName)" mkdir "$(SolutionDir)archive\$(ProjectName)"
+echo if not exist "$(SolutionDir)archive\$(ProjectName)\map.png" copy /Y "$(SolutionDir)assets\map.png" "$(SolutionDir)archive\$(ProjectName)\map.png"
+echo copy /Y "$(TargetPath)" "$(SolutionDir)archive\$(ProjectName)\$(TargetFileName)"
+</Command>
+    </PostBuildEvent>
+    <PostBuildEvent>
+      <Message>実行ファイルをフォルダにコピーしています...</Message>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="GVOConfig.h" />
+    <ClInclude Include="GVOGameProcess.h" />
+    <ClInclude Include="GVOImage.h" />
+    <ClInclude Include="GVONavish.h" />
+    <ClInclude Include="GVOSurveyCoordExtractor.h" />
+    <ClInclude Include="GVOWorldMap.h" />
+    <ClInclude Include="GVOShip.h" />
+    <ClInclude Include="Resource.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="GVOGameProcess.cpp" />
+    <ClCompile Include="GVONavish.cpp" />
+    <ClCompile Include="GVOShip.cpp" />
+    <ClCompile Include="GVOSurveyCoordExtractor.cpp" />
+    <ClCompile Include="GVOWorldMap.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="GVONavish.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="GVONavish.ico" />
+    <Image Include="small.ico" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/GVONavish/GVONavish/GVONavish.vcxproj.filters b/GVONavish/GVONavish/GVONavish.vcxproj.filters
new file mode 100644 (file)
index 0000000..5da38cd
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="ソース ファイル">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="ヘッダー ファイル">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="リソース ファイル">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="stdafx.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="targetver.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="Resource.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVONavish.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOConfig.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOGameProcess.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOImage.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOSurveyCoordExtractor.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOWorldMap.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOShip.h">
+      <Filter>ヘッダー ファイル</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>ソース ファイル</Filter>
+    </ClCompile>
+    <ClCompile Include="GVONavish.cpp">
+      <Filter>ソース ファイル</Filter>
+    </ClCompile>
+    <ClCompile Include="GVOGameProcess.cpp">
+      <Filter>ソース ファイル</Filter>
+    </ClCompile>
+    <ClCompile Include="GVOSurveyCoordExtractor.cpp">
+      <Filter>ソース ファイル</Filter>
+    </ClCompile>
+    <ClCompile Include="GVOWorldMap.cpp">
+      <Filter>ソース ファイル</Filter>
+    </ClCompile>
+    <ClCompile Include="GVOShip.cpp">
+      <Filter>ソース ファイル</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="GVONavish.rc">
+      <Filter>リソース ファイル</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="small.ico">
+      <Filter>リソース ファイル</Filter>
+    </Image>
+    <Image Include="GVONavish.ico">
+      <Filter>リソース ファイル</Filter>
+    </Image>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/GVONavish/GVONavish/GVOShip.cpp b/GVONavish/GVONavish/GVOShip.cpp
new file mode 100644 (file)
index 0000000..80c9e2d
--- /dev/null
@@ -0,0 +1,67 @@
+#include "stdafx.h"
+#include "GVOShip.h"
+#include <string>
+
+
+
+
+void GVOShip::updateWithSurveyCoord( const POINT& surveyCoord)
+{
+       // \8f\89\8aú\82Í\95û\8cü\82ð\8c\88\82ß\82ç\82ê\82È\82¢
+       if ( m_vectorHistory.empty() ) {
+               m_vector = GVOVector();
+               m_surveyCoord = surveyCoord;
+               return;
+       }
+
+       const GVOVector v( m_surveyCoord, surveyCoord );
+       const double velocity = v.length();
+
+       // \88Ú\93®\82µ\82Ä\82È\82¯\82ê\82Î\96³\8e\8b\82·\82é\81B
+       if ( velocity == 0.0 ) {
+               return;
+       }
+
+       // 90\93x\82ð\92´\82¦\82½\82ç\8eQ\8dl\82É\82È\82ç\82È\82¢\97\9a\97ð\82ð\83N\83\8a\83A\82·\82é\81B
+       const double angle = m_vector.angleTo( v );
+       if ( M_PI_2 < ::fabs( angle ) ) {
+               m_vectorHistory.clear();
+       }
+       VectorHistoryItem item;
+       item.m_surveyCoord = surveyCoord;
+       item.m_vector = v;
+       m_vectorHistory.push_back( item );
+
+       m_surveyCoord = surveyCoord;
+
+       updateVector();
+}
+
+
+void GVOShip::updateVector()
+{
+       if ( m_vectorHistory.size() < 2 ) {
+               if ( m_vectorHistory.empty() ) {
+                       return;
+               }
+               m_vector = m_vectorHistory.back().m_vector;
+               return;
+       }
+
+       // \82Æ\82è\82 \82¦\82¸\93Á\92è\82Ì\8b\97\97£\90i\82ñ\82¾\95ª\82Ì\97\9a\97ð\82ð\8d\87\90¬\82·\82é\81B
+       // \82à\82¿\82ë\82ñ\8c\8b\89Ê\82Í\88À\92è\82µ\82È\82¢\82¯\82Ç\81B
+
+       const double k_lengthThreshold = 30;    // \83Q\81[\83\80\93à\8dÀ\95W\82Å\82Ìè\87\92l
+       GVOVector v;
+       double length = 0;
+
+       for ( VectorHistory::const_reverse_iterator it = m_vectorHistory.rbegin(); it != m_vectorHistory.rend(); ++it ) {
+               v = GVOVector( v, it->m_vector );
+               length += it->m_vector.length();
+               if ( k_lengthThreshold < length ) {
+                       m_vectorHistory.erase( m_vectorHistory.begin(), std::next(it).base() );
+                       break;
+               }
+       }
+       m_vector = v;
+}
diff --git a/GVONavish/GVONavish/GVOShip.h b/GVONavish/GVONavish/GVOShip.h
new file mode 100644 (file)
index 0000000..72fb748
--- /dev/null
@@ -0,0 +1,132 @@
+#pragma once
+#include <Windows.h>
+#include <vector>
+#include <list>
+
+
+// \8aÈ\88Õ\93ñ\8e\9f\8c³\83x\83N\83g\83\8b
+class GVOVector {
+private:
+       double m_x;
+       double m_y;
+
+public:
+       GVOVector() :
+               m_x(),
+               m_y()
+       {
+       }
+       GVOVector( const double x, const double y ) :
+               m_x( x ),
+               m_y( y )
+       {
+       }
+       GVOVector( const POINT& p1, const POINT& p2 )
+       {
+               const double dx = p2.x - p1.x;
+               const double dy = p2.y - p1.y;
+               m_x = dx;
+               m_y = dy;
+       }
+       GVOVector( const GVOVector& v1, const GVOVector& v2 )
+       {
+               m_x = v1.m_x + v2.m_x;
+               m_y = v1.m_y + v2.m_y;
+       }
+
+       // \92P\88Ê\83x\83N\83g\83\8b\82Ìx\90¬\95ª
+       inline double x() const
+       {
+               return m_x;
+       }
+
+       // \92P\88Ê\83x\83N\83g\83\8b\82Ìy\90¬\95ª
+       inline double y() const
+       {
+               return m_y;
+       }
+
+       // \83x\83N\83g\83\8b\92·
+       inline double length() const
+       {
+               return ::pow( m_x*m_x + m_y*m_y, 0.5 );
+       }
+
+       inline GVOVector normalize( const double norm ) const
+       {
+               GVOVector v( m_x, m_y );
+               v.m_x /= norm;
+               v.m_y /= norm;
+               return v;
+       }
+
+       // \83x\83N\83g\83\8b\93à\90Ï
+       inline double dotProduct( const GVOVector& other ) const
+       {
+               return m_x * other.m_x + m_y * other.m_y;
+       }
+       inline double angleTo(const GVOVector& other)const
+       {
+               // Math.atan2(x1 * y0 - x0 * y1, x0 * x1 + y0 * y1);
+               return ::atan2( other.m_x * m_y - m_x * other.m_y, m_x * other.m_x + m_y * other.m_y );
+               if ( length() == 0.0 && other.length() == 0.0 ) {
+                       return NAN;
+               }
+               const double cosSita = dotProduct( other ) / (length() * other.length());
+               return ::acos( cosSita );
+       }
+private:
+       // \83x\83N\83g\83\8b\92·
+       inline double calcLength() const
+       {
+               return ::pow( m_x * m_x + m_y * m_y, 0.5 );
+       }
+};
+
+
+class GVOShip {
+       class VectorHistoryItem {
+       public:
+               POINT m_surveyCoord;
+               GVOVector m_vector;
+
+               VectorHistoryItem() :
+                       m_surveyCoord()
+               {
+               }
+       };
+       typedef std::list<VectorHistoryItem> VectorHistory;
+
+private:
+       POINT m_surveyCoord;            //!<@brief \8dÅ\90V\8dÀ\95W
+       GVOVector m_vector;                     //!<@brief \8e©\91D\82Ì\83x\83N\83g\83\8b
+       VectorHistory m_vectorHistory;  //!<@brief \83x\83N\83g\83\8b\97\9a\97ð
+
+public:
+       GVOShip() :
+               m_surveyCoord()
+       {
+       }
+
+       //!@brief \8e©\91D\82Ì\95û\8cü\82ª\97L\8cø\82©\82Ç\82¤\82©
+       inline bool isVectorEnabled() const
+       {
+               return m_vector.length() != 0.0;
+       }
+
+       //!@brief \91ª\97Ê\8dÀ\95W\82É\82æ\82é\8dÅ\90V\88Ê\92u\82ð\8dX\90V
+       void updateWithSurveyCoord( const POINT& surveyCoord);
+
+       //!@brief \8c´\93_\82Æ\83x\83N\83g\83\8b\92·\82ð\8ew\92è\82µ\82Ä\8dÀ\95W\82ð\93¾\82é
+       POINT pointFromOriginWithLength( const POINT& origin, const LONG length ) const
+       {
+               GVOVector v = m_vector.normalize(m_vector.length());
+               const POINT p = {
+                       origin.x + LONG( v.x() * length ),
+                       origin.y + LONG( v.y() * length )
+               };
+               return p;
+       }
+
+       void updateVector();
+};
diff --git a/GVONavish/GVONavish/GVOSurveyCoordExtractor.cpp b/GVONavish/GVONavish/GVOSurveyCoordExtractor.cpp
new file mode 100644 (file)
index 0000000..0c4c9af
--- /dev/null
@@ -0,0 +1,248 @@
+#include "stdafx.h"
+#include "GVONavish.h"
+#include "GVOSurveyCoordExtractor.h"
+
+namespace {
+       // \8fc11x\89¡5\83s\83N\83Z\83\8b\82Ì\93ñ\92l\89»\89æ\91\9c\82Æ\82µ\82Ä\89æ\91\9c\82ð\89ð\90Í\81B
+       const int k_numberWidth = 5;
+       const std::vector<std::string> k_sampleBits = {
+               "00111111100"
+               "01000000010"
+               "01000000010"
+               "00111111100"
+               "00000000000",  // 0
+
+               "00100000000"
+               "01111111110"
+               "00000000000"
+               "00000000000"
+               "00000000000",  // 1
+
+               "00110000110"
+               "01000011010"
+               "01000100010"
+               "00111000010"
+               "00000000000",  // 2
+
+               "00110001100"
+               "01000100010"
+               "01000100010"
+               "00111011100"
+               "00000000000",  // 3
+
+               "00000011000"
+               "00001101000"
+               "00110001000"
+               "01111111110"
+               "00000001000",  // 4
+
+               "01111101100"
+               "01001000010"
+               "01001000010"
+               "01000111100"
+               "00000000000",  // 5
+
+               "00111111100"
+               "01000100010"
+               "01000100010"
+               "00110011100"
+               "00000000000",  // 6
+
+               "01000000000"
+               "01000001110"
+               "01001110000"
+               "01110000000"
+               "00000000000",  // 7
+
+               "00111011100"
+               "01000100010"
+               "01000100010"
+               "00111011100"
+               "00000000000",  // 8
+
+               "00111001100"
+               "01000100010"
+               "01000100010"
+               "00111111100"
+               "00000000000",  // 9
+       };
+
+};
+
+
+GVOSurveyCoordExtractor::GVOSurveyCoordExtractor( const GVOImage& image )
+: m_image( image )
+, m_width( image.size().cx )
+, m_height( image.size().cy )
+, m_extractOffset()
+{
+}
+
+
+GVOSurveyCoordExtractor::~GVOSurveyCoordExtractor()
+{
+}
+
+
+std::vector<int> GVOSurveyCoordExtractor::extractNumbers()
+{
+       std::vector<int> values;
+
+       // \89ð\90Í\8f\88\97\9d\82Í\8fc11pixels\82Ì\82Ý\91Î\89\9e
+       if ( m_height == 11 ) {
+               resetExtractState();
+
+               values = extractNumbersForHeight11();
+       }
+
+#ifndef NDEBUG
+       //// \83f\83o\83b\83O\97p\82É\93ñ\92l\89»\89æ\91\9c\82É\95Ï\8a·
+       //{
+       //      const uint8_t * s = &m_binalizedImage[0];
+       //      uint8_t * d = const_cast<GVOImage&>(m_image).mutableImageBits();
+       //      for ( uint32_t i = 0; i < m_binalizedImage.size(); ++i ) {
+       //              const uint8_t v = *s++;
+       //              *d++ = v;
+       //              *d++ = v;
+       //              *d++ = v;
+       //      }
+       //}
+#endif
+       return values;
+}
+
+
+std::vector<int> GVOSurveyCoordExtractor::extractNumbersForHeight11()
+{
+       const int dxThreshold = k_numberWidth + (k_numberWidth / 2);
+       std::vector<int> values;
+       int value = -1;
+
+       while ( m_extractOffset < m_width ) {
+               const int prevOffset = m_extractOffset;
+               const int v = extractOneNumbersForHeight11();
+               const int dx = m_extractOffset - prevOffset;
+
+               if ( dxThreshold < dx ) {
+                       if ( 0 <= value ) {
+                               values.push_back( value );
+                       }
+                       value = v;
+               }
+               else {
+                       if ( value < 0 ) {
+                               value = v;
+                       }
+                       else {
+                               value = (value * 10) + v;
+                       }
+               }
+       }
+       if ( 0 <= value ) {
+               values.push_back( value );
+       }
+       return values;
+}
+
+
+int GVOSurveyCoordExtractor::extractOneNumbersForHeight11()
+{
+       const std::vector<uint8_t>& binalizedImage = binalizeImage();
+       const size_t maskLength = m_height * k_numberWidth;
+
+       bool found = false;
+       std::string bitString;
+
+       BitsDictionary candidates;
+
+       // \93ñ\92l\89»\89æ\91\9c\82ð\8fc\82É\91\96\8d¸
+       for ( uint32_t x = m_extractOffset; x < m_width; ++x ) {
+               uint32_t vert = 0;
+               std::string vertString;
+
+               for ( uint32_t y = 0; y < m_height; ++y ) {
+                       const uint8_t v = binalizedImage[y * m_width + x] ? 1 : 0;
+                       vert = (vert << 1) | v;
+                       vertString += (v) ? '1' : '0';
+               }
+               // \88ê\90F\82È\82ç\89ð\90Í\82µ\82È\82¢
+               if ( !found ) {
+                       if ( vert == 0 || vert == 0x3FF ) {
+                               continue;
+                       }
+                       found = true;
+               }
+
+               bitString += vertString;
+
+               if ( bitString.length() < maskLength ) {
+                       for ( size_t i = 0; i < k_sampleBits.size(); ++i ) {
+                               const std::string& sample = k_sampleBits[i];
+                               if ( sample.compare( 0, bitString.length(), bitString ) == 0 ) {
+                                       candidates[&sample] = i;
+                               }
+                               else {
+                                       candidates.erase( &sample );
+                               }
+                       }
+                       if ( candidates.empty() ) {
+                               bitString = "";
+                               resetCandidates( candidates );
+                       }
+                       continue;
+               }
+               else {
+                       for ( BitsDictionary::iterator it = candidates.begin(); it != candidates.end(); ++it ) {
+                               const std::string &candidate = *it->first;
+                               const int number = it->second;
+                               if ( candidate.compare( bitString ) == 0 ) {
+                                       m_extractOffset = x + 1;
+                                       return number;
+                               }
+                       }
+                       // \90\94\8e\9a\82ð\8c©\82Â\82¯\82ç\82ê\82È\82©\82Á\82½
+                       break;
+               }
+       }
+       m_extractOffset = m_width;
+       return -1;
+}
+
+
+void GVOSurveyCoordExtractor::resetExtractState()
+{
+       m_extractOffset = 0;
+}
+
+
+void GVOSurveyCoordExtractor::resetCandidates( BitsDictionary& bitsDictionary )
+{
+       bitsDictionary.clear();
+       for ( size_t i = 0; i < k_sampleBits.size(); ++i ) {
+               bitsDictionary[&k_sampleBits[i]] = i;
+       }
+}
+
+
+std::vector<uint8_t> GVOSurveyCoordExtractor::binalizeImage()
+{
+       if ( m_binalizedImage.empty() ) {
+               const uint32_t bytesPerPixel = 3;       // RGB 24bit\8c\88\82ß\91Å\82¿
+               const uint32_t stride = m_width * bytesPerPixel;
+               const uint8_t * const bits = m_image.imageBits();
+
+               std::vector<uint8_t> binalizedImage;
+               binalizedImage.resize( m_width * m_height );
+               for ( size_t i = 0; i < binalizedImage.size(); ++i ) {
+                       const uint32_t offset = i * bytesPerPixel;
+                       const uint16_t r = bits[offset + 0];
+                       const uint16_t g = bits[offset + 1];
+                       const uint16_t b = bits[offset + 2];
+                       // \93K\93\96\82É\8cv\8eZ
+                       const uint16_t total = r + g + b;
+                       binalizedImage[i] = ((240 * 3) <= total) ? 255 : 0;
+               }
+               binalizedImage.swap( m_binalizedImage );
+       }
+       return m_binalizedImage;
+}
diff --git a/GVONavish/GVONavish/GVOSurveyCoordExtractor.h b/GVONavish/GVONavish/GVOSurveyCoordExtractor.h
new file mode 100644 (file)
index 0000000..f2b5695
--- /dev/null
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <cinttypes>
+#include <vector>
+#include <map>
+
+#include "GVOImage.h"
+
+//!@brief \90\94\92l\82ð\92\8a\8fo\82·\82é\83N\83\89\83X
+class GVOSurveyCoordExtractor {
+private:
+       typedef std::map<const std::string *, int> BitsDictionary;
+
+private:
+       const GVOImage& m_image;
+       const uint32_t m_width;
+       const uint32_t m_height;
+       uint32_t m_extractOffset;
+
+       std::vector<uint8_t> m_binalizedImage;
+
+public:
+
+       GVOSurveyCoordExtractor( const GVOImage& image );
+       virtual ~GVOSurveyCoordExtractor();
+
+       std::vector<int> extractNumbers();
+
+private:
+       std::vector<int> extractNumbersForHeight11();
+       int extractOneNumbersForHeight11();
+       void resetExtractState();
+       void resetCandidates( BitsDictionary& bitsDictionary );
+       std::vector<uint8_t> binalizeImage();
+};
+
diff --git a/GVONavish/GVONavish/GVOWorldMap.cpp b/GVONavish/GVONavish/GVOWorldMap.cpp
new file mode 100644 (file)
index 0000000..98c0726
--- /dev/null
@@ -0,0 +1,370 @@
+#include "stdafx.h"
+#include <vector>
+
+#include "GVONavish.h"
+#include "GVOWorldMap.h"
+
+
+
+namespace {
+       const double k_scaleStep = 0.125;       // 12.5%
+       const double k_minScale = 0.125;        // 12.5%
+       const double k_maxScale = 4.00;         // 400%
+}
+
+
+bool GVOWorldMap::loadFromFile( const GVOConfig& config )
+{
+       std::wstring filePath;
+
+       filePath = g_makeFullPath( config.m_mapFileName );
+       if ( !m_mapImage.loadFromFile( filePath.c_str() ) ) {
+               filePath = g_makeFullPath( config.m_defaultMapFileName );
+               if ( !m_mapImage.loadFromFile( filePath.c_str() ) ) {
+                       return false;
+               }
+       }
+
+       // \89æ\91\9c\82Ì\8d\82\82³\82Æ\88Ü\93x\82Ì\94ä\97¦\82Ì\83X\83P\81[\83\8a\83\93\83O\82Ì\8aî\8f\80\82Æ\82È\82é\81B
+       const double mapHeight = m_mapImage.height();
+       m_ratioForImageCoordFromWorldCoord = mapHeight / k_worldHeight;
+       m_scaledMapSize = m_mapImage.size();
+       return true;
+}
+
+
+void GVOWorldMap::setViewSize( const SIZE& viewSize )
+{
+       m_viewSize = viewSize;
+       m_viewCenter.x = m_viewSize.cx / 2;
+       m_viewCenter.y = m_viewSize.cy / 2;
+}
+
+
+void GVOWorldMap::offsetFocusInViewCoord( const POINT& offset )
+{
+       const double dx = ((double)offset.x / m_viewScale) / m_mapImage.width();
+       const double dy = ((double)offset.y / m_viewScale) / m_mapImage.height();
+
+       LONG x = m_focusPointInWorldCoord.x + LONG( dx * k_worldWidth );
+       LONG y = m_focusPointInWorldCoord.y + LONG( dy * k_worldHeight );
+       y = max( 0, min( y, k_worldHeight ) );
+       while ( x < 0 ) {
+               x += k_worldWidth;
+       }
+       while ( k_worldWidth < x ) {
+               x -= k_worldWidth;
+       }
+
+       m_focusPointInWorldCoord.x = x;
+       m_focusPointInWorldCoord.y = y;
+}
+
+
+void GVOWorldMap::setConfig( const GVOConfig& config )
+{
+       m_positionUpdated = config.m_traceShipPositionEnabled;
+       m_focusPointInWorldCoord = config.m_initialSurveyCoord;
+       m_shipPointInWorld = config.m_initialSurveyCoord;
+       m_previousDrawPointInWorld = m_shipPointInWorld;
+}
+
+
+void GVOWorldMap::setShipPosition( const POINT& worldCoord, bool isSyncCenter )
+{
+       if ( isSyncCenter ) {
+               m_focusPointInWorldCoord = worldCoord;
+       }
+       if ( m_shipPointInWorld.x != worldCoord.x
+               || m_shipPointInWorld.y != worldCoord.y ) {
+
+               m_positionUpdated = true;
+               m_shipPointInWorld = worldCoord;
+       }
+}
+
+
+bool GVOWorldMap::zoomIn()
+{
+       double scale = m_viewScale;
+       double step = k_scaleStep;
+
+       scale = m_viewScale + step;
+       if ( k_maxScale < scale ) {
+               scale = k_maxScale;
+       }
+       if ( m_viewScale != scale ) {
+               m_viewScale = scale;
+               updateScaleCache();
+               return true;
+       }
+       return false;
+}
+
+
+bool GVOWorldMap::zoomOut()
+{
+       double scale = m_viewScale;
+       double step = k_scaleStep;
+
+       scale = m_viewScale - step;
+       if ( scale < k_minScale ) {
+               scale = k_minScale;
+       }
+       if ( m_viewScale != scale ) {
+               m_viewScale = scale;
+               updateScaleCache();
+               return true;
+       }
+       return false;
+}
+
+
+void GVOWorldMap::drawMap( HDC hdc, const GVOShip& ship )
+{
+       ::SaveDC( hdc );
+       if ( m_viewScale < 1.0 ) {
+               POINT brushOrg;
+               ::GetBrushOrgEx( hdc, &brushOrg );
+               ::SetStretchBltMode( hdc, HALFTONE );
+               ::SetBrushOrgEx( hdc, brushOrg.x, brushOrg.y, NULL );
+       }
+       else {
+               ::SetStretchBltMode( hdc, COLORONCOLOR );
+       }
+
+       HDC hdcMem = ::CreateCompatibleDC( hdc );
+       ::SaveDC( hdcMem );
+
+       ::SelectObject( hdcMem, m_mapImage.bitmapHandle() );
+
+       const SIZE mapSize = m_scaledMapSize;
+       const POINT mapTopLeft = mapOriginInView();
+
+       int xDrawOrigin, yDrawOrigin;
+       xDrawOrigin = mapTopLeft.x;
+       yDrawOrigin = mapTopLeft.y;
+
+       if ( 0 < xDrawOrigin ) {
+               xDrawOrigin = (xDrawOrigin % mapSize.cx) - mapSize.cx;
+       }
+       const int xInitial = xDrawOrigin;       // \8d\92[\82Ì\95`\89æ\8aJ\8enx\8dÀ\95W
+       int drawn = xInitial;                           // \95`\89æ\8dÏ\82Ý\8dÀ\95W
+
+       // \90¢\8aE\92n\90}\82ð\89¡\82É\95À\82×\82Ä\95`\89æ
+       // \81i\95`\89æ\8dÅ\93K\89»\82Í\8fÈ\97ª\81j
+       while ( drawn < m_viewSize.cx ) {
+               ::StretchBlt( hdc,
+                       xDrawOrigin, yDrawOrigin,
+                       mapSize.cx, mapSize.cy,
+                       hdcMem,
+                       0, 0,
+                       m_mapImage.width(), m_mapImage.height(),
+                       SRCCOPY );
+
+               xDrawOrigin += mapSize.cx;
+               drawn += mapSize.cx;
+       }
+
+
+       const POINT shipPointOffset = drawOffsetFromWorldCoord( m_shipPointInWorld );
+
+       // \90j\98H\97\\91ª\90ü\82ð\95`\89æ
+       if ( ship.isVectorEnabled() ) {
+               HPEN courseLinePen = ::CreatePen( PS_SOLID, 3, RGB( 255, 0, 0 ) );
+               HGDIOBJ oldPen = ::SelectObject( hdc, courseLinePen );
+
+               const LONG k_lineLength = k_worldHeight;
+               const POINT reachPointOffset = drawOffsetFromWorldCoord(
+                       ship.pointFromOriginWithLength( m_shipPointInWorld, k_lineLength )
+                       );
+
+               // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
+               drawn = xInitial;
+               xDrawOrigin = xInitial;
+               while ( drawn < m_viewSize.cx ) {
+                       const POINT shipPointInView = {
+                               xDrawOrigin + shipPointOffset.x,
+                               yDrawOrigin + shipPointOffset.y
+                       };
+                       const POINT reachPointInView = {
+                               xDrawOrigin + reachPointOffset.x,
+                               yDrawOrigin + reachPointOffset.y
+                       };
+                       ::MoveToEx( hdc, shipPointInView.x, shipPointInView.y, NULL );
+                       ::LineTo( hdc, reachPointInView.x, reachPointInView.y );
+                       xDrawOrigin += mapSize.cx;
+                       drawn += mapSize.cx;
+               }
+
+               ::SelectObject( hdc, oldPen );
+               ::DeleteObject( courseLinePen );
+       }
+
+       // \8e©\91D\82Ì\88Ê\92u\82ð\95`\89æ
+       const SIZE shipMarkSize = { 6, 6 };
+       HBRUSH redBrush = ::CreateSolidBrush( RGB( 255, 0, 0 ) );
+       HGDIOBJ prevBrush = ::SelectObject( hdc, redBrush );
+
+       // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
+       drawn = xInitial;
+       xDrawOrigin = xInitial;
+       while ( drawn < m_viewSize.cx ) {
+               ::Ellipse( hdc,
+                       xDrawOrigin + shipPointOffset.x - shipMarkSize.cx / 2,
+                       yDrawOrigin + shipPointOffset.y - shipMarkSize.cy / 2,
+                       xDrawOrigin + shipPointOffset.x + shipMarkSize.cx,
+                       yDrawOrigin + shipPointOffset.y + shipMarkSize.cy );
+
+               xDrawOrigin += mapSize.cx;
+               drawn += mapSize.cx;
+       }
+       ::SelectObject( hdc, prevBrush );
+       ::DeleteObject( redBrush );
+
+
+       ::RestoreDC( hdcMem, -1 );
+       ::DeleteDC( hdcMem );
+       ::RestoreDC( hdc, -1 );
+}
+
+
+void GVOWorldMap::updateShipRouteMap( HDC hdc )
+{
+       // \88Ú\93®\82µ\82Ä\82¢\82È\82¯\82ê\82Î\89½\82à\82µ\82È\82¢
+       if ( !m_positionUpdated ) {
+               return;
+       }
+       // \88Ú\93®\82µ\82Ä\82¢\82Ä\82à\8dq\98H\82ð\8cq\82°\82È\82¢\82È\82ç\95`\89æ\82µ\82È\82¢
+       if ( !m_linkRoute ) {
+               m_previousDrawPointInWorld = m_shipPointInWorld;
+               m_linkRoute = true;
+               return;
+       }
+
+       const POINT& latestPoint = imageCoordFromWorldCoord( m_shipPointInWorld );
+       const POINT& previousPoint = imageCoordFromWorldCoord( m_previousDrawPointInWorld );
+
+       // \95`\89æ\8dÀ\95W\82ª\88ê\8f\8f\82È\82ç\89½\82à\82µ\82È\82¢
+       if ( latestPoint.x == previousPoint.x
+               && latestPoint.y == previousPoint.y ) {
+               return;
+       }
+
+
+       // \8dÅ\90V\8dÀ\95W\82©\82ç\88ê\82Â\91O\82Ì\8dÀ\95W\82Ö\90ü\82ð\88ø\82­
+       // \90¢\8aE\82ð\8c×\82®\8fê\8d\87\82É\8dl\97\82ª\95K\97v\81B
+       // \88Ú\93®\97Ê\82ª\88ê\92è\82ð\92´\82¦\82½\82ç\95`\89æ\8f\88\97\9d\82µ\82È\82¢\82æ\82¤\82É\82·\82é\82Ì\82ª\83X\83}\81[\83g\82©\82È
+
+
+       // \90¢\8aE\82ð\8c×\82¢\82¾\82Æ\82Ý\82È\82·è\87\92l
+       const int k_distanceThreshold = m_mapImage.width() / 2;
+
+       const LONG xMin = min( latestPoint.x, previousPoint.x );
+       const LONG xMax = max( latestPoint.x, previousPoint.x );
+       const int xDistance = xMax - xMin;
+
+
+       HDC hdcMem = ::CreateCompatibleDC( hdc );
+       ::SaveDC( hdcMem );
+
+       HPEN hpen = ::CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
+
+       ::SelectObject( hdcMem, m_mapImage.bitmapHandle() );
+       ::SelectObject( hdcMem, hpen );
+
+       if ( k_distanceThreshold < xDistance ) {
+               if ( previousPoint.x < latestPoint.x ) {        // \90¢\8aE\82ð\90¼\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
+                       // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x3 = latestPoint.x - m_mapImage.width();
+
+                       ::MoveToEx( hdcMem, x3, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
+
+                       // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x4 = previousPoint.x + m_mapImage.width();
+                       ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, x4, previousPoint.y );
+               }
+               else {                          // \90¢\8aE\82ð\93\8c\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
+                       // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x3 = previousPoint.x - m_mapImage.width();
+
+                       ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, x3, previousPoint.y );
+
+                       // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x4 = latestPoint.x + m_mapImage.width();
+                       ::MoveToEx( hdcMem, x4, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
+               }
+       }
+       else {
+               // \8c×\82ª\82È\82¢\95`\89æ
+               ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
+               ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
+       }
+
+
+       ::RestoreDC( hdcMem, -1 );
+       ::DeleteDC( hdcMem );
+       ::DeleteObject( hpen );
+
+       m_previousDrawPointInWorld = m_shipPointInWorld;
+}
+
+
+void GVOWorldMap::clearShipRoute()
+{
+       const std::wstring fileName = m_mapImage.fileName();
+       m_mapImage.loadFromFile( fileName );
+}
+
+
+void GVOWorldMap::updateScaleCache()
+{
+       m_scaledMapSize.cx = LONG( m_mapImage.width() * m_viewScale );
+       m_scaledMapSize.cy = LONG( m_mapImage.height() * m_viewScale );
+}
+
+
+POINT GVOWorldMap::mapOriginInView()
+{
+       const POINT viewCenter = m_viewCenter;
+       const SIZE mapSize = m_scaledMapSize;
+       const POINT worldPosInView = drawOffsetFromWorldCoord( m_focusPointInWorldCoord );
+
+       POINT mapTopLeft = {
+               viewCenter.x - worldPosInView.x,
+               viewCenter.y - worldPosInView.y
+       };
+       if ( m_viewSize.cx < mapSize.cx ) {
+               while ( 0 < mapTopLeft.x ) {
+                       mapTopLeft.x -= mapSize.cx;
+               }
+       }
+
+       return mapTopLeft;
+}
+
+
+POINT GVOWorldMap::imageCoordFromWorldCoord( const POINT& worldCoord ) const
+{
+       const double xNormPos = worldCoord.x / (double)k_worldWidth;
+       const double yNormPos = worldCoord.y / (double)k_worldHeight;
+       const POINT worldPosInImage = {
+               LONG( m_mapImage.width() * xNormPos ),
+               LONG( m_mapImage.height() * yNormPos )
+       };
+       return worldPosInImage;
+}
+
+POINT GVOWorldMap::drawOffsetFromWorldCoord( const POINT&worldCoord ) const
+{
+       const POINT worldPosInImage = imageCoordFromWorldCoord( worldCoord );
+       const POINT drawOffset = {
+               LONG( worldPosInImage.x * m_viewScale ),
+               LONG( worldPosInImage.y * m_viewScale )
+       };
+       return drawOffset;
+}
diff --git a/GVONavish/GVONavish/GVOWorldMap.h b/GVONavish/GVONavish/GVOWorldMap.h
new file mode 100644 (file)
index 0000000..0b7c28c
--- /dev/null
@@ -0,0 +1,85 @@
+#pragma once
+#include <list>
+#include "GVOImage.h"
+#include "GVOShip.h"
+#include "GVOConfig.h"
+
+
+
+
+//!@brief \90¢\8aE\92n\90}
+//!@brief \95\\8e¦\8dÀ\95W\8cn\82Æ\90¢\8aE\8dÀ\95W\8cn\82Ì\95Ï\8a·\82È\82Ç\82ª\8ed\8e\96\81B
+class GVOWorldMap {
+public:
+       static const int32_t k_worldWidth = 16384;      //!<@brief \83Q\81[\83\80\90¢\8aE\82Ì\95\9d
+       static const int32_t k_worldHeight = 8192;      //!<@brief \83Q\81[\83\80\90¢\8aE\82Ì\8d\82\82³
+private:
+       //typedef std::list<POINT> ShipRouteType;
+       GVOWorldMap( const GVOWorldMap& );
+       GVOWorldMap& operator=(const GVOWorldMap&);
+
+private:
+       GVOImage m_mapImage;
+       POINT m_focusPointInWorldCoord;
+       SIZE m_viewSize;
+
+       double m_viewScale;
+       double m_ratioForImageCoordFromWorldCoord;      //!<@brief \90¢\8aE\8dÀ\95W\8cn\82©\82ç\89æ\91\9c\8dÀ\95W\8cn\82Ö\82Ì\95Ï\8a·\94ä\97¦
+
+       POINT m_shipPointInWorld;                       //!<@brief \8e©\91D\82Ì\88Ê\92u
+       bool m_positionUpdated;                         //!<@brief \8e©\91D\82Ì\88Ê\92u\8dX\90V\8fó\91Ô
+       POINT m_previousDrawPointInWorld;       //!<@brief \92¼\91O\82Ì\95`\89æ\88Ê\92u
+       bool m_linkRoute;                                       //!<@brief \8dq\98H\82ð\8cq\82°\82Ä\95`\89æ\82·\82é\83t\83\89\83O
+
+       // \8cv\8eZ\83L\83\83\83b\83V\83\85
+       SIZE m_scaledMapSize;
+       POINT m_viewCenter;
+public:
+       GVOWorldMap() :
+               m_viewScale( 1.0 ),
+               m_ratioForImageCoordFromWorldCoord(),
+               m_scaledMapSize(),
+               m_viewCenter(),
+               m_positionUpdated(),
+               m_linkRoute()
+       {
+       }
+
+       virtual ~GVOWorldMap()
+       {
+
+       }
+
+       bool loadFromFile( const GVOConfig& config );
+       void setViewSize( const SIZE& viewSize );
+       //!@note \83h\83\89\83b\83O\8f\88\97\9d\97p
+       void offsetFocusInViewCoord( const POINT& offset );
+
+       //!@brief \90Ý\92è\8fî\95ñ\82Å\8f\89\8aú\89»\82·\82é
+       void setConfig( const GVOConfig& config );
+
+       //!@brief \8e©\91D\82Ì\88Ê\92u\82ð\90Ý\92è\82·\82é
+       void setShipPosition( const POINT& worldCoord, bool isSyncCenter );
+
+       bool zoomIn();
+       bool zoomOut();
+       inline double viewScale() const
+       {
+               return m_viewScale;
+       }
+       inline double viewScaleOrder() const
+       {
+               return 100;
+       }
+
+       //!@note \82Æ\82è\82 \82¦\82¸\95`\89æ\82à\82â\82ç\82µ\82Ä\82¨\82­\81B
+       void drawMap( HDC hdc, const GVOShip& ship );
+       void updateShipRouteMap( HDC hdc );
+       void clearShipRoute();
+
+private:
+       void updateScaleCache();
+       POINT mapOriginInView();
+       POINT imageCoordFromWorldCoord( const POINT& worldCoord ) const;
+       POINT drawOffsetFromWorldCoord( const POINT&worldCoord ) const;
+};
diff --git a/GVONavish/GVONavish/ReadMe.txt b/GVONavish/GVONavish/ReadMe.txt
new file mode 100644 (file)
index 0000000..a4f28c4
--- /dev/null
@@ -0,0 +1,45 @@
+========================================================================
+    Win32 アプリケーション: GVONavish プロジェクトの概要
+========================================================================
+
+この GVONavish アプリケーションは、AppWizard により作成されました。
+
+このファイルには、GVONavish アプリケーションを構成する各ファイルの内容の概要が含まれています。
+
+
+GVONavish.vcxproj
+    これは、アプリケーション ウィザードを使用して生成された VC++ プロジェクトのメイン プロジェクト ファイルです。ファイルを生成した Visual C++ のバージョンに関する情報と、アプリケーション ウィザードで選択されたプラットフォーム、構成、およびプロジェクト機能に関する情報が含まれています。
+
+GVONavish.vcxproj.filters
+    これは、アプリケーション ウィザードで生成された VC++ プロジェクトのフィルター ファイルです。このファイルには、プロジェクト内のファイルとフィルターとの間の関連付けに関する情報が含まれています。この関連付けは、特定のノードで同様の拡張子を持つファイルのグループ化を示すために IDE で使用されます (たとえば、".cpp" ファイルは "ソース ファイル" フィルターに関連付けられています)。
+
+GVONavish.cpp
+    これは、メインのアプリケーション ソース ファイルです。
+
+/////////////////////////////////////////////////////////////////////////////
+AppWizard によって、次のリソースが作成されました。
+
+GVONavish.rc
+    これは、プログラムが使用するすべての Microsoft Windows リソースの一覧です。RES サブディレクトリに格納されるアイコン、ビットマップ、およびカーソルをインクルードしています。このファイルは、Microsoft Visual C++ で直接編集できます。
+
+Resource.h
+    これは、新しいリソース ID を定義する標準のヘッダー ファイルです。このファイルの読み込みおよび更新は、Microsoft Visual C++ で行います。
+
+GVONavish.ico
+    これは、アプリケーションのアイコン (32x32) として使用されるアイコン ファイルです。このアイコンは、メイン リソース ファイル GVONavish.rc にインクルードされます。
+
+small.ico
+    これは、アプリケーションのアイコンの小さいバージョン (16x16) を含むアイコン ファイルです。このアイコンは、メイン リソース ファイル GVONavish.rc にインクルードされます。
+
+/////////////////////////////////////////////////////////////////////////////
+その他の標準ファイル :
+
+StdAfx.h, StdAfx.cpp
+    これらのファイルは、GVONavish.pch という名前のプリコンパイル済みヘッダー (PCH) ファイルと、StdAfx.obj という名前のプリコンパイル済みの型ファイルをビルドするために使用されます。
+
+/////////////////////////////////////////////////////////////////////////////
+その他のメモ :
+
+AppWizard では "TODO:" コメントを使用して、ユーザーが追加またはカスタマイズする必要のあるソース コードを示します。
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/GVONavish/GVONavish/Resource.h b/GVONavish/GVONavish/Resource.h
new file mode 100644 (file)
index 0000000..abd5f4c
--- /dev/null
@@ -0,0 +1,34 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by GVONavish.rc
+//
+
+#define IDS_APP_TITLE                  103
+
+#define IDR_MAINFRAME                  128
+#define IDI_SMALL                              129
+#define IDD_GVONAVISH_DIALOG   102
+#define IDD_ABOUTBOX                   103
+#define IDC_GVONAVISH                  109
+#define IDC_POPUPMENU                  110
+#define IDM_ABOUT                              200
+#define IDM_EXIT                               201
+#define IDM_TOGGLE_TRACE_SHIP  202
+#define IDM_ERASE_SHIP_ROUTE   203
+
+#ifndef IDC_STATIC
+#define IDC_STATIC                             -1
+#endif
+
+// \90V\82µ\82¢\83I\83u\83W\83F\83N\83g\82Ì\8e\9f\82Ì\8aù\92è\92l
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+
+#define _APS_NO_MFC                                    130
+#define _APS_NEXT_RESOURCE_VALUE       129
+#define _APS_NEXT_COMMAND_VALUE                32771
+#define _APS_NEXT_CONTROL_VALUE                1000
+#define _APS_NEXT_SYMED_VALUE          110
+#endif
+#endif
diff --git a/GVONavish/GVONavish/small.ico b/GVONavish/GVONavish/small.ico
new file mode 100644 (file)
index 0000000..449296f
Binary files /dev/null and b/GVONavish/GVONavish/small.ico differ
diff --git a/GVONavish/GVONavish/stdafx.cpp b/GVONavish/GVONavish/stdafx.cpp
new file mode 100644 (file)
index 0000000..c7e8849
--- /dev/null
@@ -0,0 +1,8 @@
+// stdafx.cpp : \95W\8f\80\83C\83\93\83N\83\8b\81[\83h GVONavish.pch \82Ì\82Ý\82ð
+// \8aÜ\82Þ\83\\81[\83\83t\83@\83C\83\8b\82Í\81A\83v\83\8a\83R\83\93\83p\83C\83\8b\8dÏ\82Ý\83w\83b\83_\81[\82É\82È\82è\82Ü\82·\81B
+// stdafx.obj \82É\82Í\83v\83\8a\83R\83\93\83p\83C\83\8b\8dÏ\82Ý\8c^\8fî\95ñ\82ª\8aÜ\82Ü\82ê\82Ü\82·\81B
+
+#include "stdafx.h"
+
+// TODO: \82±\82Ì\83t\83@\83C\83\8b\82Å\82Í\82È\82­\81ASTDAFX.H \82Å\95K\97v\82È
+// \92Ç\89Á\83w\83b\83_\81[\82ð\8eQ\8fÆ\82µ\82Ä\82­\82¾\82³\82¢\81B
diff --git a/GVONavish/GVONavish/stdafx.h b/GVONavish/GVONavish/stdafx.h
new file mode 100644 (file)
index 0000000..ac525ae
--- /dev/null
@@ -0,0 +1,32 @@
+// stdafx.h : \95W\8f\80\82Ì\83V\83X\83e\83\80 \83C\83\93\83N\83\8b\81[\83\83t\83@\83C\83\8b\82Ì\83C\83\93\83N\83\8b\81[\83\83t\83@\83C\83\8b\81A\82Ü\82½\82Í
+// \8eQ\8fÆ\89ñ\90\94\82ª\91½\82­\81A\82©\82Â\82 \82Ü\82è\95Ï\8dX\82³\82ê\82È\82¢\81A\83v\83\8d\83W\83F\83N\83g\90ê\97p\82Ì\83C\83\93\83N\83\8b\81[\83\83t\83@\83C\83\8b
+// \82ð\8bL\8fq\82µ\82Ü\82·\81B
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define _CRT_SECURE_NO_WARNINGS
+#ifdef NDEBUG
+#define _SECURE_SCL 0
+#endif
+#define WIN32_LEAN_AND_MEAN             // Windows \83w\83b\83_\81[\82©\82ç\8eg\97p\82³\82ê\82Ä\82¢\82È\82¢\95\94\95ª\82ð\8f\9c\8aO\82µ\82Ü\82·\81B
+
+// Windows \83w\83b\83_\81\83t\83@\83C\83\8b:
+#include <windows.h>
+#include <objbase.h>
+#include <MMSystem.h>
+#pragma comment(lib, "winmm.lib")
+
+// C \83\89\83\93\83^\83C\83\80 \83w\83b\83_\81\83t\83@\83C\83\8b
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <tchar.h>
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#include <cstdint>
+#include <numeric>
diff --git a/GVONavish/GVONavish/targetver.h b/GVONavish/GVONavish/targetver.h
new file mode 100644 (file)
index 0000000..1a56f7a
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+// SDKDDKVer.h \82ð\83C\83\93\83N\83\8b\81[\83h\82·\82é\82Æ\81A\97\98\97p\82Å\82«\82é\8dÅ\82à\8fã\88Ê\82Ì Windows \83v\83\89\83b\83g\83t\83H\81[\83\80\82ª\92è\8b`\82³\82ê\82Ü\82·\81B
+
+// \88È\91O\82Ì Windows \83v\83\89\83b\83g\83t\83H\81[\83\80\97p\82É\83A\83v\83\8a\83P\81[\83V\83\87\83\93\82ð\83r\83\8b\83h\82·\82é\8fê\8d\87\82Í\81AWinSDKVer.h \82ð\83C\83\93\83N\83\8b\81[\83h\82µ\81A
+// SDKDDKVer.h \82ð\83C\83\93\83N\83\8b\81[\83h\82·\82é\91O\82É\81A\83T\83|\81[\83g\91Î\8fÛ\82Æ\82·\82é\83v\83\89\83b\83g\83t\83H\81[\83\80\82ð\8e¦\82·\82æ\82¤\82É _WIN32_WINNT \83}\83N\83\8d\82ð\90Ý\92è\82µ\82Ü\82·\81B
+
+#define _WIN32_WINNT 0x0501    // XP
+#include <SDKDDKVer.h>
diff --git a/GVONavish/assets/map.png b/GVONavish/assets/map.png
new file mode 100644 (file)
index 0000000..4993c7a
Binary files /dev/null and b/GVONavish/assets/map.png differ
diff --git a/readme.md b/readme.md
new file mode 100644 (file)
index 0000000..a902cfb
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,32 @@
+# GVONavish
+
+『大航海時代 Online』に関わる著作権、その他一切の知的財産権は、株式会社コーエーテクモゲームスに帰属します。
+
+---
+
+GVONaviが極北でちゃんと動かなくなったので、GVONaviっぽく動くアプリを模倣して作ってみました。
+
+オープンソースなので今後のアップデートで放置されても有志が変更できます。
+
+
+## 開発環境
+
+* C/C++
+* Visual Studio 2013 Express Update 1
+* Windows 7
+
+VS2013があればそのままビルドできるはずです。
+
+外部ライブラリはOS標準のもの以外使用していません。
+
+## ライセンス
+
+本アプリはオープンソースです。
+
+フォークしたアプリもオープンソースであればいんじゃないでしょうか。
+
+
+## 謝辞
+
+* 測量座標解析は交易マップC#のソースを参考にしました。
+* 世界地図は統括wikiの最新版を使用しました。